Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Compiler_Typed_Sema.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
58#ifndef COMPILER_TYPED_SEMA_H
59#define COMPILER_TYPED_SEMA_H
60
61#include <sstream>
62#include <string>
63
64#include <Compiler_Sema.H>
65#include <Compiler_Types.H>
66#include <tpl_constraints.H>
67#include <tpl_scope.H>
68
69namespace Aleph {
76
83
90
98
107
110{
116
123
129
136
142
144 {
145 std::string name;
148 bool resolving = false;
149 bool resolved = false;
150 };
151
166
167 void emit_error(const Source_Span &span,
168 const std::string &code,
169 const std::string &message,
170 const std::string &note = "",
171 const std::string &help = "") const
172 {
173 if (diagnostics == nullptr)
174 return;
175
176 auto builder = diagnostics->error(span, message).code(code);
177 if (not note.empty())
178 builder.note(note);
179 if (not help.empty())
180 builder.help(help);
181 builder.emit();
182 }
183
184 bool is_invalid_type(const Compiler_Type_Id id) const noexcept
185 {
186 return id == 0 or id == types.invalid_type();
187 }
188
193
194 void record_expr_type(const Compiler_Expr *expr, const Compiler_Type_Id type_id)
195 {
196 if (expr == nullptr)
197 return;
198
199 for (size_t i = expr_types.size(); i > 0; --i)
200 if (expr_types.access(i - 1).expr == expr)
201 {
202 expr_types.access(i - 1).type_id = type_id;
203 return;
204 }
205
206 expr_types.append({expr, type_id});
207 }
208
210 {
211 if (function == nullptr)
212 return;
213
214 for (size_t i = function_types.size(); i > 0; --i)
215 if (function_types.access(i - 1).function == function)
216 {
217 function_types.access(i - 1).type_id = type_id;
218 return;
219 }
220
221 function_types.append({function, type_id});
222 }
223
224 void record_let_type(const Compiler_Let_Stmt *stmt, const Compiler_Type_Id type_id)
225 {
226 if (stmt == nullptr)
227 return;
228
229 for (size_t i = let_types.size(); i > 0; --i)
230 if (let_types.access(i - 1).stmt == stmt)
231 {
232 let_types.access(i - 1).type_id = type_id;
233 return;
234 }
235
236 let_types.append({stmt, type_id});
237 }
238
240 const size_t index,
241 const Compiler_Type_Id type_id)
242 {
243 for (size_t i = param_types.size(); i > 0; --i)
244 if (param_types.access(i - 1).function == function and param_types.access(i - 1).index == index)
245 {
246 param_types.access(i - 1).type_id = type_id;
247 return;
248 }
249
250 param_types.append({function, index, type_id});
251 }
252
254 const Compiler_Type_Id target) noexcept
255 {
256 for (size_t i = 0; i < values.size(); ++i)
257 if (values.access(i) == target)
258 return true;
259 return false;
260 }
261
263 {
264 if (target != 0 and not contains_type_id(values, target))
265 values.append(target);
266 }
267
268 static bool contains_name(const DynArray<std::string> &names, const std::string &target) noexcept
269 {
270 for (size_t i = 0; i < names.size(); ++i)
271 if (names.access(i) == target)
272 return true;
273 return false;
274 }
275
277 {
278 scopes.enter_scope();
280 }
281
283 {
284 scopes.leave_scope();
285 const auto start = scope_markers.size() > 0 ? scope_markers.pop() : 0;
286 active_bindings.cut(start);
287 }
288
289 bool insert_binding(const std::string &name, const Compiler_Value_Binding &binding)
290 {
291 const auto inserted = scopes.insert(name, binding);
292 if (inserted)
293 active_bindings.append({name, binding});
294 return inserted;
295 }
296
298 {
299 for (size_t i = module_type_bindings.size(); i > 0; --i)
300 if (module_type_bindings.access(i - 1).name == name)
301 return &module_type_bindings.access(i - 1);
302 return nullptr;
303 }
304
305 const Compiler_Module_Type_Binding *find_module_type_binding(const std::string &name) const noexcept
306 {
307 for (size_t i = module_type_bindings.size(); i > 0; --i)
308 if (module_type_bindings.access(i - 1).name == name)
309 return &module_type_bindings.access(i - 1);
310 return nullptr;
311 }
312
313 Compiler_Type_Id lookup_type_name(const std::string &name,
314 const DynArray<Compiler_Type_Name_Binding> &bindings) const noexcept
315 {
316 for (size_t i = bindings.size(); i > 0; --i)
317 if (bindings.access(i - 1).name == name)
318 return bindings.access(i - 1).type_id;
319 return 0;
320 }
321
322 Compiler_Type_Id resolve_builtin_type_name(const std::string &name) const noexcept
323 {
324 if (name == "Invalid")
325 return types.invalid_type();
326 if (name == "Unit")
327 return types.unit_type();
328 if (name == "Bool")
329 return types.bool_type();
330 if (name == "Int" or name == "Integer")
331 return types.integer_type();
332 if (name == "String")
333 return types.string_type();
334 if (name == "Char" or name == "Character")
335 return types.character_type();
336 return 0;
337 }
338
340 const Source_Span &span)
341 {
342 if (binding.resolved)
343 return binding.type_id;
344
345 if (binding.alias_decl == nullptr)
346 {
347 binding.resolved = true;
348 return binding.type_id;
349 }
350
351 if (binding.resolving)
352 {
353 emit_error(span, "TYP010", "cyclic type alias involving '" + binding.name + "'");
354 binding.type_id = types.invalid_type();
355 binding.resolved = true;
356 return binding.type_id;
357 }
358
359 binding.resolving = true;
361 binding.type_id
363 binding.resolving = false;
364 binding.resolved = true;
365 return binding.type_id;
366 }
367
368 Compiler_Type_Id resolve_module_type_name(const std::string &name, const Source_Span &span)
369 {
370 auto *binding = find_module_type_binding(name);
371 if (binding == nullptr)
372 return 0;
373
374 if (binding->alias_decl != nullptr)
375 return resolve_alias_binding(*binding, span);
376
377 binding->resolved = true;
378 return binding->type_id;
379 }
380
382 const std::string &candidate,
383 const Source_Span &span,
384 const std::string &what,
385 const std::string &owner)
386 {
387 if (contains_name(names, candidate))
388 {
389 emit_error(span, "TYP011", "duplicate " + what + " '" + candidate + "' in '" + owner + "'");
390 return false;
391 }
392 return true;
393 }
394
396 {
397 if (module == nullptr)
398 return;
399
400 for (size_t i = 0; i < module->type_declarations.size(); ++i)
401 {
402 const auto *decl = module->type_declarations.access(i);
403 if (decl == nullptr or decl->kind == Compiler_Type_Decl_Kind::Invalid)
404 continue;
405
406 if (find_module_type_binding(decl->name) != nullptr)
407 {
408 emit_error(decl->name_span,
409 "TYP008",
410 "duplicate type declaration of '" + decl->name + "'");
411 continue;
412 }
413
415 binding.name = decl->name;
416
417 switch (decl->kind)
418 {
420 binding.alias_decl = static_cast<const Compiler_Type_Alias_Decl *>(decl);
421 break;
422
424 binding.type_id = types.make_struct_type(decl->name);
425 binding.resolved = false;
426 break;
427
429 binding.type_id = types.make_enum_type(decl->name);
430 binding.resolved = false;
431 break;
432
434 break;
435 }
436
437 module_type_bindings.append(std::move(binding));
438 }
439 }
440
442 {
443 if (decl == nullptr)
444 return;
445
446 auto *binding = find_module_type_binding(decl->name);
447 if (binding == nullptr or binding->resolved)
448 return;
449
450 switch (decl->kind)
451 {
453 return;
454
456 (void) resolve_alias_binding(*binding, decl->name_span);
457 return;
458
460 {
461 const auto *node = static_cast<const Compiler_Struct_Decl *>(decl);
463 DynArray<Compiler_Type_Id> field_types;
464 for (size_t i = 0; i < node->fields.size(); ++i)
465 {
466 const auto &field = node->fields.access(i);
468 field_names, field.name, field.name_span, "field", node->name))
469 continue;
470
472 field_names.append(field.name);
473 field_types.append(resolve_type_annotation(field.annotation, no_type_variables, false));
474 }
475
476 types.set_struct_fields(binding->type_id, field_names, field_types);
477 binding->resolved = true;
478 return;
479 }
480
482 {
483 const auto *node = static_cast<const Compiler_Enum_Decl *>(decl);
485 for (size_t i = 0; i < node->variants.size(); ++i)
486 {
487 const auto &variant = node->variants.access(i);
489 variant_names, variant.name, variant.name_span, "enum variant", node->name))
490 variant_names.append(variant.name);
491 }
492
493 types.set_enum_variants(binding->type_id, variant_names);
494 binding->resolved = true;
495 return;
496 }
497 }
498 }
499
501 {
502 if (module == nullptr)
503 return;
504
505 for (size_t i = 0; i < module->type_declarations.size(); ++i)
507 }
508
511 const bool allow_fresh_type_variables = true)
512 {
513 if (type_expr == nullptr)
514 return types.invalid_type();
515
516 switch (type_expr->kind)
517 {
519 return types.invalid_type();
520
522 {
523 const auto *node = static_cast<const Compiler_Named_Type_Expr *>(type_expr);
524 if (const auto builtin = resolve_builtin_type_name(node->name); builtin != 0)
525 return builtin;
526
527 if (const auto known = lookup_type_name(node->name, type_names); known != 0)
528 return known;
529
530 if (const auto declared = resolve_module_type_name(node->name, node->name_span);
531 declared != 0)
532 return declared;
533
535 {
536 emit_error(node->name_span, "TYP009", "unknown type name '" + node->name + "'");
537 return types.invalid_type();
538 }
539
540 const auto variable = types.make_type_variable(node->name, true);
541 type_names.append({node->name, variable});
542 return variable;
543 }
544
546 {
547 const auto *node = static_cast<const Compiler_Tuple_Type_Expr *>(type_expr);
549 for (size_t i = 0; i < node->members.size(); ++i)
550 members.append(resolve_type_annotation(node->members.access(i),
553 return types.make_tuple_type(members);
554 }
555
557 {
558 const auto *node = static_cast<const Compiler_Function_Type_Expr *>(type_expr);
560 for (size_t i = 0; i < node->parameters.size(); ++i)
561 params.append(resolve_type_annotation(node->parameters.access(i),
566 }
567 }
568
569 return types.invalid_type();
570 }
571
574 {
575 if (function == nullptr)
576 return;
577
578 for (size_t i = 0; i < type_names.size(); ++i)
580 {function, type_names.access(i).name, type_names.access(i).type_id});
581 }
582
585 {
586 if (function == nullptr)
587 return;
588
589 for (size_t i = 0; i < function_annotation_type_variables.size(); ++i)
590 if (function_annotation_type_variables.access(i).function == function)
591 type_names.append({function_annotation_type_variables.access(i).name,
592 function_annotation_type_variables.access(i).type_id});
593 }
594
596 const DynArray<Compiler_Type_Id> &quantified_variables,
598 DynArray<Compiler_Type_Id> &replacements)
599 {
600 const auto resolved = unifier.apply(type_id);
601 if (is_invalid_type(resolved))
602 return resolved;
603
604 if (types.is_builtin(resolved))
605 return resolved;
606
607 const auto &type = types.type(resolved);
608 if (type.kind == Compiler_Type_Kind::Type_Variable)
609 {
610 for (size_t i = 0; i < quantified_variables.size(); ++i)
611 if (quantified_variables.access(i) == resolved)
612 {
613 for (size_t j = 0; j < originals.size() and j < replacements.size(); ++j)
614 if (originals.access(j) == resolved)
615 return replacements.access(j);
616
617 const auto fresh = fresh_type();
618 originals.append(resolved);
619 replacements.append(fresh);
620 return fresh;
621 }
622
623 return resolved;
624 }
625
626 if (type.kind == Compiler_Type_Kind::Tuple)
627 {
629 for (size_t i = 0; i < type.components.size(); ++i)
630 members.append(
631 instantiate_type(type.components.access(i), quantified_variables, originals, replacements));
632 return types.make_tuple_type(members);
633 }
634
635 if (type.kind == Compiler_Type_Kind::Function)
636 {
638 for (size_t i = 0; i < type.components.size(); ++i)
640 instantiate_type(type.components.access(i), quantified_variables, originals, replacements));
641 const auto result
642 = instantiate_type(type.result_type, quantified_variables, originals, replacements);
643 return types.make_function_type(params, result);
644 }
645
646 if (type.kind == Compiler_Type_Kind::Struct or type.kind == Compiler_Type_Kind::Enum)
647 return resolved;
648
649 return resolved;
650 }
651
653 {
654 if (not binding.polymorphic)
655 return unifier.apply(binding.type_id);
656
658 DynArray<Compiler_Type_Id> replacements;
659 return instantiate_type(binding.scheme.type_id,
661 originals,
662 replacements);
663 }
664
667 {
668 const auto resolved = unifier.apply(type_id);
669 if (is_invalid_type(resolved) or types.is_builtin(resolved))
670 return;
671
672 const auto &type = types.type(resolved);
673 switch (type.kind)
674 {
678 return;
679
682 return;
683
685 for (size_t i = 0; i < type.components.size(); ++i)
686 collect_free_type_variables(type.components.access(i), free_variables);
687 return;
688
690 for (size_t i = 0; i < type.components.size(); ++i)
691 collect_free_type_variables(type.components.access(i), free_variables);
693 return;
694 }
695 }
696
712
714 {
716 for (size_t i = active_bindings.size(); i > 0; --i)
717 {
718 const auto &entry = active_bindings.access(i - 1);
719 if (contains_name(seen_names, entry.name))
720 continue;
721
722 seen_names.append(entry.name);
724 }
725 }
726
729 = {})
730 {
731 Compiler_Type_Scheme scheme;
732 scheme.type_id = unifier.apply(type_id);
733
734 for (size_t i = 0; i < explicit_quantified_variables.size(); ++i)
735 append_unique_type_id(scheme.quantified_variables, explicit_quantified_variables.access(i));
736
739
742
743 for (size_t i = 0; i < free_type_variables.size(); ++i)
744 {
745 const auto variable = free_type_variables.access(i);
746 const auto &type = types.type(variable);
747 if (type.kind != Compiler_Type_Kind::Type_Variable)
748 continue;
749
750 if (type.rigid)
751 continue;
752
754 continue;
755
756 append_unique_type_id(scheme.quantified_variables, variable);
757 }
758
759 return scheme;
760 }
761
762 Compiler_Type_Id lookup_name_type(const std::string &name, const Source_Span &span)
763 {
764 if (const auto *binding = scopes.lookup(name); binding != nullptr)
765 return instantiate_binding(*binding);
766
768 emit_error(span, "TYP001", "use of undeclared identifier '" + name + "'");
769 return types.invalid_type();
770 }
771
773 const Compiler_Type_Id rhs,
774 const Source_Span &span,
775 const std::string &code,
776 const std::string &prefix)
777 {
778 if (is_invalid_type(lhs) or is_invalid_type(rhs))
779 return false;
780
781 const auto result = unifier.unify(lhs, rhs);
782 if (result.ok())
783 return true;
784
785 emit_error(span, code, prefix + ": " + result.message);
786 return false;
787 }
788
790 {
793 for (size_t i = 0; i < function->parameters.size(); ++i)
794 {
795 const auto &param = function->parameters.access(i);
796 const auto param_type = param.annotation != nullptr
798 : fresh_type();
799 params.append(param_type);
800 record_param_type(function, i, param_type);
801 }
802
803 const auto result_type = function->return_annotation != nullptr
805 : fresh_type();
806 const auto function_type = types.make_function_type(params, result_type);
809
811 binding.type_id = function_type;
812 if (type_names.size() > 0)
813 {
814 binding.polymorphic = true;
815 binding.scheme.type_id = function_type;
816 for (size_t i = 0; i < type_names.size(); ++i)
818 }
819
820 (void) insert_binding(function->name, binding);
821 return function_type;
822 }
823
825 {
826 for (size_t i = function_types.size(); i > 0; --i)
827 if (function_types.access(i - 1).function == function)
828 return function_types.access(i - 1).type_id;
829 return 0;
830 }
831
833 {
834 if (expr == nullptr)
835 return types.invalid_type();
836
838 switch (expr->kind)
839 {
841 result = types.invalid_type();
842 break;
843
845 result = types.integer_type();
846 break;
847
849 result = types.string_type();
850 break;
851
853 result = types.character_type();
854 break;
855
857 result = types.bool_type();
858 break;
859
861 {
862 const auto *node = static_cast<const Compiler_Identifier_Expr *>(expr);
863 result = lookup_name_type(node->name, node->name_span);
864 break;
865 }
866
868 {
869 const auto *node = static_cast<const Compiler_Grouping_Expr *>(expr);
870 result = analyze_expr(node->inner);
871 break;
872 }
873
875 {
876 const auto *node = static_cast<const Compiler_Unary_Expr *>(expr);
877 const auto operand_type = analyze_expr(node->operand);
878
879 switch (node->op)
880 {
886 node->operator_span,
887 "TYP003",
888 "unary operator '" + std::string(compiler_token_kind_name(node->op))
889 + "' requires an Int operand");
890 result = types.integer_type();
891 break;
892
896 node->operator_span,
897 "TYP003",
898 "unary operator '!' requires a Bool operand");
899 result = types.bool_type();
900 break;
901
902 default:
903 result = fresh_type();
904 break;
905 }
906 break;
907 }
908
910 {
911 const auto *node = static_cast<const Compiler_Binary_Expr *>(expr);
912 const auto left_type = analyze_expr(node->left);
913 const auto right_type = analyze_expr(node->right);
914
915 switch (node->op)
916 {
932 node->operator_span,
933 "TYP004",
934 "binary operator '" + std::string(compiler_token_kind_name(node->op))
935 + "' requires Int operands");
938 node->operator_span,
939 "TYP004",
940 "binary operator '" + std::string(compiler_token_kind_name(node->op))
941 + "' requires Int operands");
942 result = types.integer_type();
943 break;
944
951 node->operator_span,
952 "TYP004",
953 "comparison operator '" + std::string(compiler_token_kind_name(node->op))
954 + "' requires Int operands");
957 node->operator_span,
958 "TYP004",
959 "comparison operator '" + std::string(compiler_token_kind_name(node->op))
960 + "' requires Int operands");
961 result = types.bool_type();
962 break;
963
968 node->operator_span,
969 "TYP004",
970 "logical operator '" + std::string(compiler_token_kind_name(node->op))
971 + "' requires Bool operands");
974 node->operator_span,
975 "TYP004",
976 "logical operator '" + std::string(compiler_token_kind_name(node->op))
977 + "' requires Bool operands");
978 result = types.bool_type();
979 break;
980
986 node->operator_span,
987 "TYP004",
988 "binary operator '" + std::string(compiler_token_kind_name(node->op))
989 + "' requires compatible operand types");
990 result = node->op == Compiler_Token_Kind::Assign ? left_type : types.bool_type();
991 break;
992
993 default:
994 result = fresh_type();
995 break;
996 }
997 break;
998 }
999
1001 {
1002 const auto *node = static_cast<const Compiler_Call_Expr *>(expr);
1003 const auto callee_type = analyze_expr(node->callee);
1005 {
1006 for (size_t i = 0; i < node->arguments.size(); ++i)
1007 (void) analyze_expr(node->arguments.access(i));
1008 result = types.invalid_type();
1009 break;
1010 }
1011
1013 for (size_t i = 0; i < node->arguments.size(); ++i)
1014 argument_types.append(analyze_expr(node->arguments.access(i)));
1015
1016 result = fresh_type();
1019 callee_type, callable_type, node->span, "TYP005", "call expression constraint failed");
1020 break;
1021 }
1022 }
1023
1024 record_expr_type(expr, result);
1025 return result;
1026 }
1027
1031 bool &saw_return)
1032 {
1033 if (block == nullptr)
1034 return;
1035
1036 enter_scope();
1037 for (size_t i = 0; i < block->statements.size(); ++i)
1038 analyze_stmt(block->statements.access(i), current_return_type, current_function, saw_return);
1039 leave_scope();
1040 }
1041
1042 void analyze_stmt(const Compiler_Stmt *stmt,
1045 bool &saw_return)
1046 {
1047 if (stmt == nullptr)
1048 return;
1049
1050 switch (stmt->kind)
1051 {
1053 return;
1054
1056 (void) analyze_expr(static_cast<const Compiler_Expr_Stmt *>(stmt)->expr);
1057 return;
1058
1060 {
1061 const auto *node = static_cast<const Compiler_Let_Stmt *>(stmt);
1064
1065 const auto annotation_type = node->annotation != nullptr
1066 ? resolve_type_annotation(node->annotation, type_names)
1067 : Compiler_Type_Id(0);
1068 const auto initializer_type
1069 = node->initializer != nullptr ? analyze_expr(node->initializer) : Compiler_Type_Id(0);
1070
1072 if (annotation_type != 0 and initializer_type != 0)
1073 {
1076 node->name_span,
1077 "TYP007",
1078 "initializer is incompatible with the declared binding type");
1079 type_id = annotation_type;
1080 }
1081 else if (annotation_type != 0)
1082 type_id = annotation_type;
1083 else if (initializer_type != 0)
1084 type_id = initializer_type;
1085 else
1086 type_id = fresh_type();
1087
1088 const auto scheme = generalize_type(type_id,
1089 [&type_names]()
1090 {
1092 for (size_t i = 0; i < type_names.size(); ++i)
1093 quantified.append(type_names.access(i).type_id);
1094 return quantified;
1095 }());
1096
1097 Compiler_Value_Binding binding;
1098 binding.type_id = scheme.type_id;
1099 binding.polymorphic = scheme.quantified_variables.size() > 0;
1100 binding.scheme = scheme;
1101
1102 (void) insert_binding(node->name, binding);
1103 record_let_type(node, binding.type_id);
1104 return;
1105 }
1106
1108 {
1109 const auto *node = static_cast<const Compiler_Return_Stmt *>(stmt);
1110 saw_return = true;
1111 const auto value_type
1112 = node->value != nullptr ? analyze_expr(node->value) : types.unit_type();
1113 if (current_return_type != 0)
1116 value_type,
1117 node->keyword_span,
1118 "TYP006",
1119 "return expression is incompatible with the inferred function result type");
1120 return;
1121 }
1122
1124 analyze_block(static_cast<const Compiler_Block_Stmt *>(stmt),
1127 saw_return);
1128 return;
1129
1131 {
1132 const auto *node = static_cast<const Compiler_If_Stmt *>(stmt);
1133 const auto condition_type = analyze_expr(node->condition);
1135 types.bool_type(),
1136 node->if_span,
1137 "TYP002",
1138 "if condition must have type Bool");
1141 return;
1142 }
1143
1145 {
1146 const auto *node = static_cast<const Compiler_While_Stmt *>(stmt);
1147 const auto condition_type = analyze_expr(node->condition);
1149 types.bool_type(),
1150 node->keyword_span,
1151 "TYP002",
1152 "while condition must have type Bool");
1154 return;
1155 }
1156
1159 return;
1160 }
1161 }
1162
1164 {
1165 if (function == nullptr)
1166 return;
1167
1168 const auto function_type = find_function_type(function);
1169 if (function_type == 0)
1170 return;
1171
1172 const auto &type_node = types.type(function_type);
1173 const auto return_type = type_node.result_type;
1174
1175 enter_scope();
1176 for (size_t i = 0; i < function->parameters.size(); ++i)
1177 {
1178 const auto param_type = type_node.components.access(i);
1179 Compiler_Value_Binding binding;
1180 binding.type_id = param_type;
1181 (void) insert_binding(function->parameters.access(i).name, binding);
1182 }
1183
1184 bool saw_return = false;
1185 analyze_block(function->body, return_type, function, saw_return);
1186
1189 types.unit_type(),
1190 function->name_span,
1191 "TYP006",
1192 "function without explicit returns defaults to Unit");
1193
1194 leave_scope();
1195 }
1196
1198 {
1199 for (size_t i = 0; i < expr_types.size(); ++i)
1200 expr_types.access(i).type_id = unifier.apply(expr_types.access(i).type_id);
1201 for (size_t i = 0; i < function_types.size(); ++i)
1202 function_types.access(i).type_id = unifier.apply(function_types.access(i).type_id);
1203 for (size_t i = 0; i < let_types.size(); ++i)
1204 let_types.access(i).type_id = unifier.apply(let_types.access(i).type_id);
1205 for (size_t i = 0; i < param_types.size(); ++i)
1206 param_types.access(i).type_id = unifier.apply(param_types.access(i).type_id);
1207 }
1208
1209public:
1217 : diagnostics(dx), options(opts), base_semantic(dx, opts.semantic_options), unifier(types)
1218 {}
1219
1222 {
1224 types.clear();
1225 unifier.clear();
1226 scopes.clear();
1227 expr_types.clear();
1228 function_types.clear();
1229 let_types.clear();
1230 param_types.clear();
1232 active_bindings.clear();
1234 module_type_bindings.clear();
1235 }
1236
1242 {
1243 clear();
1244 if (module == nullptr)
1245 return;
1246
1249
1252
1253 enter_scope();
1254
1255 for (size_t i = 0; i < module->functions.size(); ++i)
1256 (void) predeclare_function(module->functions.access(i));
1257
1258 bool ignored_return = false;
1259 for (size_t i = 0; i < module->statements.size(); ++i)
1260 analyze_stmt(module->statements.access(i), 0, nullptr, ignored_return);
1261
1262 for (size_t i = 0; i < module->functions.size(); ++i)
1263 analyze_function(module->functions.access(i));
1264
1265 leave_scope();
1267 }
1268
1274
1277 {
1278 return types;
1279 }
1280
1288 Compiler_Type_Id declared_type(const std::string &name) const noexcept
1289 {
1290 if (const auto *binding = find_module_type_binding(name); binding != nullptr)
1291 return binding->type_id;
1292 return 0;
1293 }
1294
1297 {
1298 return unifier;
1299 }
1300
1303 {
1304 for (size_t i = expr_types.size(); i > 0; --i)
1305 if (expr_types.access(i - 1).expr == expr)
1306 return expr_types.access(i - 1).type_id;
1307 return 0;
1308 }
1309
1312 {
1313 for (size_t i = function_types.size(); i > 0; --i)
1314 if (function_types.access(i - 1).function == function)
1315 return function_types.access(i - 1).type_id;
1316 return 0;
1317 }
1318
1320 Compiler_Type_Id let_type(const Compiler_Let_Stmt *stmt) const noexcept
1321 {
1322 for (size_t i = let_types.size(); i > 0; --i)
1323 if (let_types.access(i - 1).stmt == stmt)
1324 return let_types.access(i - 1).type_id;
1325 return 0;
1326 }
1327
1330 const size_t index) const noexcept
1331 {
1332 for (size_t i = param_types.size(); i > 0; --i)
1333 if (param_types.access(i - 1).function == function and param_types.access(i - 1).index == index)
1334 return param_types.access(i - 1).type_id;
1335 return 0;
1336 }
1337
1339 std::string dump_inference() const
1340 {
1341 std::ostringstream out;
1342 out << "Typed Semantic Analysis\n";
1343
1344 if (module_type_bindings.size() > 0)
1345 {
1346 out << "Types\n";
1347 for (size_t i = 0; i < module_type_bindings.size(); ++i)
1348 {
1349 const auto &entry = module_type_bindings.access(i);
1350 out << " " << entry.name << ": ";
1351 if (entry.type_id != 0)
1352 out << types.to_string(entry.type_id);
1353 else
1354 out << "<unresolved>";
1355 out << '\n';
1356 }
1357 }
1358
1359 if (function_types.size() > 0)
1360 {
1361 out << "Functions\n";
1362 for (size_t i = 0; i < function_types.size(); ++i)
1363 {
1364 const auto &entry = function_types.access(i);
1365 out << " " << entry.function->name << ": " << types.to_string(entry.type_id) << '\n';
1366 }
1367 }
1368
1369 if (param_types.size() > 0)
1370 {
1371 out << "Parameters\n";
1372 for (size_t i = 0; i < param_types.size(); ++i)
1373 {
1374 const auto &entry = param_types.access(i);
1375 out << " " << entry.function->name << "."
1376 << entry.function->parameters.access(entry.index).name << ": "
1377 << types.to_string(entry.type_id) << '\n';
1378 }
1379 }
1380
1381 if (let_types.size() > 0)
1382 {
1383 out << "Bindings\n";
1384 for (size_t i = 0; i < let_types.size(); ++i)
1385 {
1386 const auto &entry = let_types.access(i);
1387 out << " " << entry.stmt->name << ": " << types.to_string(entry.type_id) << " @"
1388 << entry.stmt->name_span.to_string() << '\n';
1389 }
1390 }
1391
1392 return out.str();
1393 }
1394};
1395} // namespace Aleph
1396
1397#endif // COMPILER_TYPED_SEMA_H
Name-resolution and basic semantic checks for the compiler-support MVP.
Stable type graph for the compiler-support MVP.
size_t size_t int32_t * out
Definition ca-c-api.h:120
Name-resolution and basic semantic checker for the MVP AST.
void clear() noexcept
Clears previously collected semantic state.
void analyze_module(const Compiler_Module *module)
Analyzes a parsed module.
Context owning all compiler type nodes.
void set_struct_fields(const Compiler_Type_Id id, const DynArray< std::string > &field_names, const DynArray< Compiler_Type_Id > &field_types) const
Assigns field metadata to a previously created struct type.
bool is_builtin(const Compiler_Type_Id id) const
Returns whether id names a built-in type.
Compiler_Type_Id string_type() const noexcept
Returns the preloaded String type id.
std::string to_string(const Compiler_Type_Id id) const
Renders one type to a deterministic human-readable string.
Compiler_Type_Id make_enum_type(std::string name)
Creates a nominal enum type placeholder.
const Compiler_Type & type(const Compiler_Type_Id id) const
Returns type id.
Compiler_Type_Id invalid_type() const noexcept
Returns the preloaded Invalid type id.
void set_enum_variants(const Compiler_Type_Id id, const DynArray< std::string > &variant_names) const
Assigns variant metadata to a previously created enum type.
Compiler_Type_Id make_type_variable(std::string label="", const bool rigid=false)
Creates a fresh type variable.
Compiler_Type_Id make_tuple_type(const DynArray< Compiler_Type_Id > &members)
Creates a tuple type.
Compiler_Type_Id make_function_type(const DynArray< Compiler_Type_Id > &parameters, const Compiler_Type_Id result)
Creates a function type.
Compiler_Type_Id character_type() const noexcept
Returns the preloaded Char type id.
void clear() noexcept
Drops user-created types and keeps the built-ins.
Compiler_Type_Id unit_type() const noexcept
Returns the preloaded Unit type id.
Compiler_Type_Id bool_type() const noexcept
Returns the preloaded Bool type id.
Compiler_Type_Id integer_type() const noexcept
Returns the preloaded Int type id.
Compiler_Type_Id make_struct_type(std::string name)
Creates a nominal struct type placeholder.
Structural unifier for compiler types.
Compiler_Unify_Result unify(const Compiler_Type_Id lhs, const Compiler_Type_Id rhs)
Attempts to unify lhs and rhs.
Compiler_Type_Id apply(const Compiler_Type_Id id)
Applies the current substitution to id.
void clear() noexcept
Clears substitutions and the last stored result.
Inference-oriented semantic pass for the MVP compiler front-end.
void predeclare_module_type_declarations(const Compiler_Module *module)
const Compiler_Semantic_Analyzer & semantic_analysis() const noexcept
Returns the base name-resolution pass used by the analyzer.
DynArray< Compiler_Expr_Type_Assignment > expr_types
const Compiler_Type_Unifier & type_unifier() const noexcept
Returns the internal unifier state.
void remember_function_annotation_type_variables(const Compiler_Function_Decl *function, const DynArray< Compiler_Type_Name_Binding > &type_names)
void analyze_module(const Compiler_Module *module)
Runs the typed semantic analysis for module.
static bool contains_type_id(const DynArray< Compiler_Type_Id > &values, const Compiler_Type_Id target) noexcept
Compiler_Type_Id let_type(const Compiler_Let_Stmt *stmt) const noexcept
Returns the inferred type of one let binding, or 0 if unknown.
Compiler_Type_Id find_function_type(const Compiler_Function_Decl *function) const
DynArray< Compiler_Function_Annotation_Type_Variable > function_annotation_type_variables
void record_let_type(const Compiler_Let_Stmt *stmt, const Compiler_Type_Id type_id)
DynArray< Compiler_Let_Type_Assignment > let_types
void analyze_block(const Compiler_Block_Stmt *block, const Compiler_Type_Id current_return_type, const Compiler_Function_Decl *current_function, bool &saw_return)
Compiler_Type_Id instantiate_binding(const Compiler_Value_Binding &binding)
void collect_free_type_variables(const Compiler_Type_Id type_id, DynArray< Compiler_Type_Id > &free_variables)
Compiler_Type_Id resolve_builtin_type_name(const std::string &name) const noexcept
Compiler_Type_Id lookup_type_name(const std::string &name, const DynArray< Compiler_Type_Name_Binding > &bindings) const noexcept
void clear() noexcept
Clears all inferred state and substitutions.
Compiler_Type_Id resolve_alias_binding(Compiler_Module_Type_Binding &binding, const Source_Span &span)
Compiler_Type_Id analyze_expr(const Compiler_Expr *expr)
Compiler_Type_Id inferred_type(const Compiler_Expr *expr) const noexcept
Returns the inferred type of one expression, or 0 if unknown.
bool validate_unique_member_name(const DynArray< std::string > &names, const std::string &candidate, const Source_Span &span, const std::string &what, const std::string &owner)
Compiler_Type_Id function_type(const Compiler_Function_Decl *function) const noexcept
Returns the inferred type of one function, or 0 if unknown.
void analyze_stmt(const Compiler_Stmt *stmt, const Compiler_Type_Id current_return_type, const Compiler_Function_Decl *current_function, bool &saw_return)
Compiler_Type_Id resolve_module_type_name(const std::string &name, const Source_Span &span)
DynArray< Compiler_Module_Type_Binding > module_type_bindings
Compiler_Type_Scheme generalize_type(const Compiler_Type_Id type_id, const DynArray< Compiler_Type_Id > &explicit_quantified_variables={})
static bool contains_name(const DynArray< std::string > &names, const std::string &target) noexcept
Compiler_Type_Id parameter_type(const Compiler_Function_Decl *function, const size_t index) const noexcept
Returns the inferred type of one parameter, or 0 if unknown.
Compiler_Typed_Semantic_Analyzer(Diagnostic_Engine *dx=nullptr, const Compiler_Typed_Semantic_Options &opts={})
Constructs a typed semantic analyzer.
void collect_binding_free_type_variables(const Compiler_Value_Binding &binding, DynArray< Compiler_Type_Id > &free_variables)
DynArray< Compiler_Function_Type_Assignment > function_types
bool insert_binding(const std::string &name, const Compiler_Value_Binding &binding)
void resolve_module_type_declaration(const Compiler_Type_Decl *decl)
bool require_unify(const Compiler_Type_Id lhs, const Compiler_Type_Id rhs, const Source_Span &span, const std::string &code, const std::string &prefix)
bool is_invalid_type(const Compiler_Type_Id id) const noexcept
Compiler_Module_Type_Binding * find_module_type_binding(const std::string &name) noexcept
DynArray< Compiler_Param_Type_Assignment > param_types
Scope< std::string, Compiler_Value_Binding > scopes
static void append_unique_type_id(DynArray< Compiler_Type_Id > &values, const Compiler_Type_Id target)
Compiler_Type_Id declared_type(const std::string &name) const noexcept
Returns one declared module type by name, or 0 if unknown.
Compiler_Type_Id lookup_name_type(const std::string &name, const Source_Span &span)
void resolve_module_type_declarations(const Compiler_Module *module)
void emit_error(const Source_Span &span, const std::string &code, const std::string &message, const std::string &note="", const std::string &help="") const
Compiler_Type_Id resolve_type_annotation(const Compiler_Type_Expr *type_expr, DynArray< Compiler_Type_Name_Binding > &type_names, const bool allow_fresh_type_variables=true)
DynArray< Compiler_Active_Binding > active_bindings
void record_param_type(const Compiler_Function_Decl *function, const size_t index, const Compiler_Type_Id type_id)
void analyze_function(const Compiler_Function_Decl *function)
const Compiler_Module_Type_Binding * find_module_type_binding(const std::string &name) const noexcept
std::string dump_inference() const
Dumps inferred function, parameter, and binding types.
Compiler_Type_Id predeclare_function(const Compiler_Function_Decl *function)
Compiler_Type_Id instantiate_type(const Compiler_Type_Id type_id, const DynArray< Compiler_Type_Id > &quantified_variables, DynArray< Compiler_Type_Id > &originals, DynArray< Compiler_Type_Id > &replacements)
void collect_environment_free_type_variables(DynArray< Compiler_Type_Id > &free_variables)
void seed_function_annotation_type_variables(const Compiler_Function_Decl *function, DynArray< Compiler_Type_Name_Binding > &type_names) const
Compiler_Typed_Semantic_Options options
const Compiler_Type_Context & type_context() const noexcept
Returns the internal type context.
void record_expr_type(const Compiler_Expr *expr, const Compiler_Type_Id type_id)
void record_function_type(const Compiler_Function_Decl *function, const Compiler_Type_Id type_id)
Diagnostic_Builder & code(const std::string &value)
Sets the stable diagnostic code.
size_t emit() const noexcept
Finalizes the builder and returns the diagnostic index.
Diagnostic_Builder & help(const std::string &msg)
Appends a help line.
Diagnostic_Builder & note(const std::string &msg)
Appends a note line.
Diagnostic accumulator and renderer.
Diagnostic_Builder error(const Source_Span &span, const std::string &msg)
Starts an error diagnostic.
void clear() noexcept
Empties the container.
size_t size() const noexcept
Return the current dimension of array.
T pop()
Remove the last item of array (as if this was a stack)
T & access(const size_t i) const noexcept
Fast access without checking allocation and bound_min_clock checking.
T & append()
Allocate a new entry to the end of array.
Lexical scope stack for Key to Value associations.
Definition tpl_scope.H:88
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
@ Grouping
Parenthesized expression.
@ Unary
Prefix unary operation (e.g., -x, !p).
@ Identifier
Variable or function name reference.
@ Integer_Literal
Numeric integer constant.
@ Invalid
Placeholder for malformed expressions.
@ Char_Literal
Character constant.
@ Binary
Infix binary operation (e.g., x + y).
@ Call
Function or method call.
@ String_Literal
String constant.
@ Bool_Literal
Boolean constant (true/false).
const char * compiler_token_kind_name(Compiler_Token_Kind kind)
Returns a human-readable name for a token kind.
void message(const char *file, int line, const char *format,...)
Print an informational message with file and line info.
Definition ahDefs.C:95
@ While
Loop while a condition is true.
@ Invalid
Placeholder for malformed statements.
@ Expr
Expression evaluated for side effects (e.g., assignment, call).
@ If
Conditional execution.
@ Return
Exit current function with an optional value.
@ Continue
Skip to the next iteration of the innermost loop.
@ Let
Variable declaration and optional initialization.
@ Block
Scoped sequence of statements.
@ Break
Immediate exit from the innermost loop.
@ Invalid
Placeholder for malformed type declarations.
@ Struct
Nominal struct declaration.
@ Enum
Nominal enum declaration.
@ Alias
Transparent type alias declaration.
and
Check uniqueness with explicit hash + equality functors.
std::string code(Node *root)
Compute a string with the Lukasiewicz`s word of a tree.
static void prefix(Node *root, DynList< Node * > &acc)
size_t Compiler_Type_Id
@ Named
Named type such as Int or T.
@ Invalid
Placeholder for malformed type syntax.
@ Function
Function type such as fn(Int) -> Bool.
@ Tuple
Tuple type such as (Int, Bool).
Node representing an infix binary operation.
Node representing a braced sequence of statements.
Node representing a function or method call.
Nominal top-level enum declaration with unit variants.
Node representing an expression evaluated as a statement.
Compiler_Expr * expr
The expression being evaluated.
Recorded inferred type for one expression node.
Compiler_Type_Id type_id
Inferred or constrained type.
const Compiler_Expr * expr
Expression node.
Abstract base class for expression nodes.
Compiler_Expr_Kind kind
Specific expression type.
Node representing a top-level function declaration.
Source_Span name_span
Location of the function name token.
std::string name
Function name.
DynArray< Compiler_Param > parameters
Ordered list of parameters.
Compiler_Type_Expr * return_annotation
Optional declared return type.
Compiler_Block_Stmt * body
Scoped body of the function.
Recorded inferred type for one function declaration.
const Compiler_Function_Decl * function
Function declaration.
Compiler_Type_Id type_id
Inferred function type.
Function type syntax such as fn(Int, T) -> Bool.
Node representing an expression explicitly wrapped in parentheses.
Node representing a named identifier reference.
Node representing a conditional branch.
Node representing a local variable binding.
Recorded inferred type for one let binding.
Compiler_Type_Id type_id
Binding type.
const Compiler_Let_Stmt * stmt
Let statement node.
Node representing a complete translation unit or module.
DynArray< Compiler_Type_Decl * > type_declarations
Top-level type declarations.
DynArray< Compiler_Stmt * > statements
Optional top-level code.
DynArray< Compiler_Function_Decl * > functions
Top-level function definitions.
Named type syntax such as Int, Bool, or T.
Recorded inferred type for one function parameter.
const Compiler_Function_Decl * function
Owner function.
size_t index
Parameter index inside the function.
Compiler_Type_Id type_id
Parameter type.
Node representing a return from the current function.
Compiler_Expr * value
Optional value being returned.
Options controlling semantic analyzer behavior.
Abstract base class for statement nodes.
Compiler_Stmt_Kind kind
Specific statement type.
Nominal top-level struct declaration.
Tuple type syntax such as (Int, Bool).
Transparent top-level type alias declaration.
Compiler_Type_Expr * aliased_type
Aliased target type expression.
Abstract base class for top-level type declarations.
Abstract base class for parsed type-annotation syntax.
Compiler_Type_Id result_type
Function return type, or 0.
Options controlling the typed semantic pass.
bool infer_unit_from_missing_return
Constrain functions with no explicit return to Unit.
Compiler_Semantic_Options semantic_options
Options forwarded to the base semantic pass.
bool run_base_semantic_checks
Run Compiler_Sema.H before typing.
Node representing a prefix unary operation.
Node representing a while-loop.
Half-open byte range inside a source file.
Definition ah-source.H:100
Equality constraints and type unification helpers for compiler work.
Lexical scope management using frame stacks.