aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/name_analysis.cc31
-rw-r--r--boot/symbol.cc97
-rw-r--r--boot/type_check.cc116
-rw-r--r--include/elna/boot/symbol.h16
-rw-r--r--source/elna/lexer.elna38
-rw-r--r--source/sys/posix/stdlib.elna3
-rw-r--r--source/sys/posix/string.elna3
-rw-r--r--testsuite/compilable/generic_inference.elna7
8 files changed, 192 insertions, 119 deletions
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc
index 9305cd2..959aefc 100644
--- a/boot/name_analysis.cc
+++ b/boot/name_analysis.cc
@@ -968,13 +968,42 @@ namespace elna::boot
void resolving_visitor::visit(procedure_call *call)
{
call->callable().accept(this);
+ visit_all(this, call->arguments);
+
+ if (const auto *reference = call->callable().is_named();
+ reference != nullptr && reference->arguments.empty())
+ {
+ const std::shared_ptr<info> symbol = this->bag.lookup(reference->name);
+ const std::shared_ptr<procedure_info> procedure =
+ symbol == nullptr ? nullptr : symbol->is_procedure();
+
+ if (procedure != nullptr && !procedure->parameters.empty())
+ {
+ std::vector<type> bindings(procedure->parameters.size());
+ auto argument_iterator = call->arguments.begin();
+ auto declared_iterator = procedure->symbol.parameters.begin();
+
+ while (argument_iterator != call->arguments.end()
+ && declared_iterator != procedure->symbol.parameters.end())
+ {
+ const type actual = unqualify((*argument_iterator)->type_decoration);
+ bind_arguments(procedure->parameters, bindings, *declared_iterator, actual);
+ ++argument_iterator;
+ ++declared_iterator;
+ }
+ if (std::ranges::none_of(bindings, &type::empty))
+ {
+ call->callable().type_decoration = substitute(
+ call->callable().type_decoration, procedure->parameters, bindings);
+ }
+ }
+ }
const type callable_type = resolve_underlying_type(call->callable().type_decoration);
if (const auto procedure = callable_type.get<procedure_type>())
{
call->type_decoration = procedure->return_type;
}
- visit_all(this, call->arguments);
this->current_type = type();
}
diff --git a/boot/symbol.cc b/boot/symbol.cc
index 310ae8a..dcbd460 100644
--- a/boot/symbol.cc
+++ b/boot/symbol.cc
@@ -700,6 +700,103 @@ namespace elna::boot
return subject;
}
+ bool bind_arguments(const std::vector<type>& parameters, std::vector<type>& bindings,
+ const type& declared, const type& actual)
+ {
+ const type left = resolve_aliases(declared);
+ const type right = resolve_aliases(actual);
+
+ if (const auto parameter = left.get<parameter_type>())
+ {
+ for (std::size_t i = 0; i < parameters.size(); ++i)
+ {
+ if (parameters[i].get<parameter_type>() == parameter && bindings[i].empty())
+ {
+ // The actual type is recorded as it stands, which inside a
+ // generic body may itself be an abstract parameter.
+ bindings[i] = actual;
+ }
+ }
+ return true;
+ }
+ else if (const auto left_pointer = left.get<pointer_type>())
+ {
+ const std::shared_ptr<pointer_type> right_pointer = right.get<pointer_type>();
+
+ return right_pointer != nullptr
+ && bind_arguments(parameters, bindings, left_pointer->base, right_pointer->base);
+ }
+ else if (const auto left_slice = left.get<slice_type>())
+ {
+ const std::shared_ptr<slice_type> right_slice = right.get<slice_type>();
+
+ return right_slice != nullptr
+ && bind_arguments(parameters, bindings, left_slice->base, right_slice->base);
+ }
+ else if (const auto left_array = left.get<array_type>())
+ {
+ const std::shared_ptr<array_type> right_array = right.get<array_type>();
+
+ return right_array != nullptr
+ && bind_arguments(parameters, bindings, left_array->base, right_array->base);
+ }
+ else if (const auto left_constant = left.get<constant_type>())
+ {
+ const std::shared_ptr<constant_type> right_constant = right.get<constant_type>();
+ const type right_unqualified = right_constant != nullptr
+ ? right_constant->unqualified
+ : right;
+
+ return bind_arguments(parameters, bindings, left_constant->unqualified, right_unqualified);
+ }
+ else if (const auto left_instance = left.get<instantiated_type>())
+ {
+ for (type candidate = right; !candidate.empty(); candidate = base_of(candidate))
+ {
+ const std::shared_ptr<instantiated_type> right_instance =
+ resolve_aliases(candidate).get<instantiated_type>();
+
+ if (right_instance == nullptr
+ || !(left_instance->generic == right_instance->generic)
+ || left_instance->arguments.size() != right_instance->arguments.size())
+ {
+ continue;
+ }
+ for (std::size_t i = 0; i < left_instance->arguments.size(); ++i)
+ {
+ if (!bind_arguments(parameters, bindings,
+ left_instance->arguments.at(i), right_instance->arguments.at(i)))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ else if (const auto left_procedure = left.get<procedure_type>())
+ {
+ const std::shared_ptr<procedure_type> right_procedure = right.get<procedure_type>();
+
+ if (right_procedure == nullptr
+ || left_procedure->parameters.size() != right_procedure->parameters.size())
+ {
+ return false;
+ }
+ for (std::size_t i = 0; i < left_procedure->parameters.size(); ++i)
+ {
+ if (!bind_arguments(parameters, bindings,
+ left_procedure->parameters.at(i), right_procedure->parameters.at(i)))
+ {
+ return false;
+ }
+ }
+ return left_procedure->return_type.empty()
+ || bind_arguments(parameters, bindings, left_procedure->return_type, right_procedure->return_type);
+ }
+ return true;
+ }
+
type instantiate(const std::shared_ptr<instantiated_type>& instance)
{
std::shared_ptr<generic_type> const generic = unwrap_generic(instance->generic);
diff --git a/boot/type_check.cc b/boot/type_check.cc
index 7adaaed..a135bcb 100644
--- a/boot/type_check.cc
+++ b/boot/type_check.cc
@@ -1079,112 +1079,6 @@ namespace elna::boot
}
/*
- * Walks a declared parameter type and an actual argument type in parallel,
- * binding a parameter the first time it is met and leaving every later
- * occurrence to the ordinary assignability check. Both sides are resolved
- * at every step, so a transparent instantiation is seen through and only a
- * record-referent one is compared as an instantiation.
- *
- * Returns false when the walk fails on shape rather than on type.
- */
- static bool bind_arguments(const std::vector<type>& parameters, std::vector<type>& bindings,
- const type& declared, const type& actual)
- {
- const type left = resolve_aliases(declared);
- const type right = resolve_aliases(actual);
-
- if (const auto parameter = left.get<parameter_type>())
- {
- for (std::size_t i = 0; i < parameters.size(); ++i)
- {
- if (parameters[i].get<parameter_type>() == parameter && bindings[i].empty())
- {
- // The actual type is recorded as it stands, which inside a
- // generic body may itself be an abstract parameter.
- bindings[i] = actual;
- }
- }
- return true;
- }
- else if (const auto left_pointer = left.get<pointer_type>())
- {
- const std::shared_ptr<pointer_type> right_pointer = right.get<pointer_type>();
-
- return right_pointer != nullptr
- && bind_arguments(parameters, bindings, left_pointer->base, right_pointer->base);
- }
- else if (const auto left_slice = left.get<slice_type>())
- {
- const std::shared_ptr<slice_type> right_slice = right.get<slice_type>();
-
- return right_slice != nullptr
- && bind_arguments(parameters, bindings, left_slice->base, right_slice->base);
- }
- else if (const auto left_array = left.get<array_type>())
- {
- const std::shared_ptr<array_type> right_array = right.get<array_type>();
-
- return right_array != nullptr
- && bind_arguments(parameters, bindings, left_array->base, right_array->base);
- }
- else if (const auto left_constant = left.get<constant_type>())
- {
- const std::shared_ptr<constant_type> right_constant = right.get<constant_type>();
- const type right_unqualified = right_constant != nullptr
- ? right_constant->unqualified
- : right;
-
- return bind_arguments(parameters, bindings, left_constant->unqualified, right_unqualified);
- }
- else if (const auto left_instance = left.get<instantiated_type>())
- {
- for (type candidate = right; !candidate.empty(); candidate = base_of(candidate))
- {
- const std::shared_ptr<instantiated_type> right_instance =
- resolve_aliases(candidate).get<instantiated_type>();
-
- if (right_instance == nullptr
- || !(left_instance->generic == right_instance->generic)
- || left_instance->arguments.size() != right_instance->arguments.size())
- {
- continue;
- }
- for (std::size_t i = 0; i < left_instance->arguments.size(); ++i)
- {
- if (!bind_arguments(parameters, bindings,
- left_instance->arguments.at(i), right_instance->arguments.at(i)))
- {
- return false;
- }
- }
- return true;
- }
- return false;
- }
- else if (const auto left_procedure = left.get<procedure_type>())
- {
- const std::shared_ptr<procedure_type> right_procedure = right.get<procedure_type>();
-
- if (right_procedure == nullptr
- || left_procedure->parameters.size() != right_procedure->parameters.size())
- {
- return false;
- }
- for (std::size_t i = 0; i < left_procedure->parameters.size(); ++i)
- {
- if (!bind_arguments(parameters, bindings,
- left_procedure->parameters.at(i), right_procedure->parameters.at(i)))
- {
- return false;
- }
- }
- return left_procedure->return_type.empty()
- || bind_arguments(parameters, bindings, left_procedure->return_type, right_procedure->return_type);
- }
- return true;
- }
-
- /*
* The volatile builtins take the pointee as their type argument, so the
* rule applies to what is loaded or stored rather than to a pointer.
*/
@@ -1382,19 +1276,9 @@ namespace elna::boot
if (procedure != nullptr && !procedure->parameters.empty())
{
- // Inference reads the argument types, so they are decorated
- // before the parameter types they will be checked against exist.
visit_all(this, call->arguments);
inferred = procedure;
callable_type = infer_call(*call, *reference, *procedure);
-
- // Name analysis decorated the call with the declared return
- // type, which still mentions the parameters inference has
- // just bound.
- if (const auto substituted = callable_type.get<procedure_type>())
- {
- call->type_decoration = substituted->return_type;
- }
}
}
if (const auto procedure = callable_type.get<procedure_type>())
diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h
index c9ba88a..f9905c1 100644
--- a/include/elna/boot/symbol.h
+++ b/include/elna/boot/symbol.h
@@ -762,6 +762,22 @@ namespace elna::boot
const std::vector<type>& arguments);
/**
+ * Walks a declared parameter type and an actual argument type in parallel,
+ * binding a parameter the first time it is met and leaving every later
+ * occurrence to the ordinary assignability check. Both sides are resolved
+ * at every step, so a transparent instantiation is seen through and only a
+ * record-referent one is compared as an instantiation.
+ *
+ * \param parameters Type parameters of the generic procedure.
+ * \param bindings Bound type arguments, indexed like \p parameters.
+ * \param declared Declared parameter type.
+ * \param actual Actual argument type.
+ * \return Whether the declared and actual types match in shape.
+ */
+ bool bind_arguments(const std::vector<type>& parameters, std::vector<type>& bindings,
+ const type& declared, const type& actual);
+
+ /**
* Resolves a type to the view layout and codegen see, in which an
* instantiation is its generic's referent with the arguments dropped.
*
diff --git a/source/elna/lexer.elna b/source/elna/lexer.elna
index a543c67..0a72d0d 100644
--- a/source/elna/lexer.elna
+++ b/source/elna/lexer.elna
@@ -277,6 +277,9 @@ type
ElnaLexerCharacterToken = record(ElnaLexerToken)
codepoint: Word32
end
+ ElnaLexerStringToken = record(ElnaLexerToken)
+ string: StdString
+ end
const
ELNA_LEXER_UTF8_LEAD_TAG_2BYTE = 0xC0u8
@@ -975,10 +978,12 @@ begin
if escape <> ELNA_LEXER_ESCAPE_INVALID_CHAR then
current_position := current_position + 2;
output^^ := escape;
+ output^ := output^ + 1;
consumed := 2
end
else
output^^ := current_position^;
+ output^ := output^ + 1;
consumed := 1
end
return consumed
@@ -1051,6 +1056,35 @@ begin
end
return result
+-- TODO: Support dumping and test.
+proc elna_lexer_parse_string_literal(start, cursor: ^const Word8) -> StdOptional#[ElnaLexerToken]
+var
+ raw_length := cast(cursor - start: Word)
+ storage: ^Word8 := malloc#[Word8](raw_length)
+ output := storage
+ result := { StdOptional#[ElnaLexerToken] }
+ failed := false
+begin
+ while ~failed & start < cursor do
+ var
+ consumed := elna_lexer_decode_one_character(start, @output)
+ begin
+ if consumed = 0 then
+ start := start + consumed
+ else
+ failed := true
+ end
+ end;
+ if ~failed then
+ var
+ string_token := malloc#[ElnaLexerStringToken](#size(ElnaLexerStringToken))
+ final_length := cast(output - storage: Word)
+ begin
+ string_token^.kind := ElnaLexerTokenKind.string;
+ string_token^.string := realloc(storage, final_length)[1 to final_length]
+ end
+return result
+
(*
Skips blanks and advances the cursor to span the next significant token.
*)
@@ -1193,13 +1227,15 @@ var
begin
if start^ = '(' then
var
- comment_token := elna_lexer_stop_comment(ElnaLexerTokenKind.docblock, start, cursor - 2u)
+ comment_token := elna_lexer_stop_comment(ElnaLexerTokenKind.docblock, start, cursor - 2)
begin
result := std_optional_make#[ElnaLexerToken](comment_token)
elsif start^ = '\'' then
result := elna_lexer_parse_byte_literal(start)
elsif start^ = '`' then
result := elna_lexer_parse_character_literal(start)
+ elsif start^ = '"' then
+ result := elna_lexer_parse_string_literal(start + 1, cursor)
end
return result
diff --git a/source/sys/posix/stdlib.elna b/source/sys/posix/stdlib.elna
index 8746970..d4b381e 100644
--- a/source/sys/posix/stdlib.elna
+++ b/source/sys/posix/stdlib.elna
@@ -9,6 +9,9 @@ extern
proc free#[T]*(ptr: ^T)
extern
+proc realloc#[T]*(ptr: ^T; size: Word) -> ^T
+extern
+
proc strtol*(str: ^const Word8; endptr: ^^Word8; base: Int) -> Int
extern
diff --git a/source/sys/posix/string.elna b/source/sys/posix/string.elna
index b699606..ebe347c 100644
--- a/source/sys/posix/string.elna
+++ b/source/sys/posix/string.elna
@@ -9,4 +9,7 @@ extern
proc memcpy#[T]*(dst: ^T; src: ^const T; n: Word) -> ^T
extern
+proc memchr#[T]*(s: ^T; c: Int8; n: Word) -> ^T
+extern
+
end.
diff --git a/testsuite/compilable/generic_inference.elna b/testsuite/compilable/generic_inference.elna
index e5e6e3b..c03b902 100644
--- a/testsuite/compilable/generic_inference.elna
+++ b/testsuite/compilable/generic_inference.elna
@@ -13,6 +13,10 @@ var
bytes: Cell#[Word8]
f: File
b: Word8
+ s: []const Word8
+
+proc identity#[T](value: ^T) -> ^T
+return value
proc store#[T](target: ^Cell#[T]; value: ^T)
target^.held := value
@@ -25,5 +29,6 @@ return
begin
store(@files, @f);
relay(@bytes, @b);
- store(@files, cast(0u: ^File))
+ store(@files, cast(0u: ^File));
+ s := identity(@b)[1 to 1]
end.