Yume
type_walker.cpp
Go to the documentation of this file.
1#include "type_walker.hpp"
2#include "ast/ast.hpp"
5#include "compiler/vals.hpp"
8#include "ty/substitution.hpp"
9#include "ty/type.hpp"
10#include "util.hpp"
11#include <algorithm>
12#include <cstdint>
13#include <functional>
14#include <limits>
15#include <llvm/ADT/STLExtras.h>
16#include <llvm/ADT/StringMap.h>
17#include <llvm/ADT/StringMapEntry.h>
18#include <llvm/ADT/iterator.h>
19#include <llvm/IR/Constants.h>
20#include <llvm/IR/Use.h>
21#include <llvm/Support/Casting.h>
22#include <llvm/Support/ErrorHandling.h>
23#include <llvm/Support/raw_ostream.h>
24#include <memory>
25#include <optional>
26#include <ranges>
27#include <stdexcept>
28#include <utility>
29#include <vector>
30
31namespace yume::semantic {
32inline void wrap_in_implicit_cast(ast::OptionalExpr& expr, ty::Conv conv, optional<ty::Type> target_type) {
33 auto cast_expr = std::make_unique<ast::ImplicitCastExpr>(expr->token_range(), move(expr), conv);
34 cast_expr->val_ty(target_type);
35 expr = move(cast_expr);
36}
37
38inline auto try_implicit_conversion(ast::OptionalExpr& expr, optional<ty::Type> target_ty) -> bool {
39 if (!target_ty)
40 return false;
41 if (!expr)
42 return false;
43
44 auto expr_ty = expr->ensure_ty();
45 auto compat = expr_ty.compatibility(*target_ty);
46 if (!compat.valid)
47 return false;
48
49 if (!compat.conv.empty())
50 wrap_in_implicit_cast(expr, compat.conv, target_ty);
51
52 return true;
53}
54
55void make_implicit_conversion(ast::OptionalExpr& expr, optional<ty::Type> target_ty) {
56 if (!target_ty)
57 return;
58 if (!expr)
59 return;
60
61 if (!try_implicit_conversion(expr, target_ty)) {
62 throw std::runtime_error("Invalid implicit conversion ('"s + expr->ensure_ty().name() + "' -> '" +
63 target_ty->name() + "', " + std::to_string(expr->ensure_ty().kind()) + " -> " +
64 std::to_string(target_ty->kind()) + ")");
65 }
66}
67
68template <> void TypeWalker::expression(ast::NumberExpr& expr) {
69 auto val = expr.val;
70 if (val > std::numeric_limits<int32_t>::max())
71 expr.val_ty(compiler.m_types.int64().s_ty);
72 else
73 expr.val_ty(compiler.m_types.int32().s_ty);
74}
75
76template <> void TypeWalker::expression(ast::StringExpr& expr) {
77 // TODO(rymiel): #18 String type
78 expr.val_ty(create_slice_type(ty::Type(compiler.m_types.int8().u_ty)));
79}
80
81template <> void TypeWalker::expression(ast::CharExpr& expr) { expr.val_ty(compiler.m_types.int8().u_ty); }
82
83template <> void TypeWalker::expression(ast::BoolExpr& expr) { expr.val_ty(compiler.m_types.bool_type); }
84
85template <> void TypeWalker::expression(ast::Type& expr) {
86 expr.val_ty(convert_type(expr));
87 if (auto* qual_type = dyn_cast<ast::QualType>(&expr))
88 expression(*qual_type->base);
89}
90
91template <> void TypeWalker::expression(ast::TypeName& expr) {
92 auto& type = *expr.type;
93 expr.attach_to(&type);
94 expression(type);
95}
96
97template <> void TypeWalker::expression(ast::ImplicitCastExpr& expr) { body_expression(*expr.base); }
98
99static inline auto for_all_instantiations(std::deque<Struct>& structs, std::invocable<Struct&> auto fn) {
100 for (auto& i : structs) {
101 fn(i);
102 for (auto& [k, v] : i.instantiations)
103 fn(*v);
104 }
105}
106
107template <> void TypeWalker::expression(ast::CtorExpr& expr) {
108 Struct* st = nullptr;
109
110 expression(*expr.type);
111 auto base_type = convert_type(*expr.type);
112
113 for_all_instantiations(compiler.m_structs, [&](Struct& i) {
114 if (i.self_ty == base_type)
115 st = &i;
116 });
117 expr.val_ty(base_type);
118
119 const bool consider_ctor_overloads = st != nullptr;
120 OverloadSet ctor_overloads{};
121
122 // XXX: Duplicated from function overload handling
123 if (consider_ctor_overloads) {
125 ctor_overloads = all_ctor_overloads_by_type(*st, expr);
126 }
127
128 for (auto& i : expr.args) {
129 body_expression(*i);
130 ctor_overloads.args.push_back(i.raw_ptr());
131 }
132
133 if (consider_ctor_overloads) {
134#ifdef YUME_SPEW_OVERLOAD_SELECTION
135 errs() << "\n*** BEGIN CTOR OVERLOAD EVALUATION ***\n";
136 errs() << "Constructors with matching types:\n";
137 ctor_overloads.dump(errs());
138#endif
139
140 ctor_overloads.determine_valid_overloads();
141
142#ifdef YUME_SPEW_OVERLOAD_SELECTION
143 errs() << "\nViable overloads:\n";
144 ctor_overloads.dump(errs(), true);
145#endif
146
147 auto best_overload = ctor_overloads.best_viable_overload();
148
149#ifdef YUME_SPEW_OVERLOAD_SELECTION
150 errs() << "\nSelected overload:\n";
151 best_overload.dump(errs());
152 errs() << "\n*** END CTOR OVERLOAD EVALUATION ***\n\n";
153#endif
154
155 auto& subs = best_overload.subs;
156 auto* selected = best_overload.fn;
157 YUME_ASSERT(subs.fully_substituted(), "Constructors cannot be generic"); // TODO(rymiel): revisit?
158
159 // XXX: STILL Duplicated from function overload handling
160 // It is an instantiation of a function template
161 if (!subs.empty()) {
162 // Try to find an already existing instantiation with the same substitutions
163 auto [already_existed, inst_fn] = selected->get_or_create_instantiation(subs);
164 if (!already_existed) {
165 // An existing one wasn't found. We've been given a duplicate of the template's AST but without types
166 // The duplicate will have its types set again according to the substitutions being used.
167 auto& new_fn = inst_fn;
168
169 // The types of the instantiated function must be set immediately (i.e. with in_depth = false)
170 // This is because the implicit cast logic below depends on the direct type being set here and bound type
171 // information doesn't propagate across ImplicitCastExpr...
172 // TODO(rymiel): Find a better solution; such as moving cast logic also into queue?
173 // However, that would require evaluation of the queue very eagerly (i.e. immediately when the function is used,
174 // which kinda defeats the purpose of the queue). So I guess we'll just keep this until I think of a better
175 // solution
176 // TODO(rymiel): find a better solution other than the solution proposed above
177 with_saved_scope([&] {
178 in_depth = false;
179 current_decl = &new_fn;
180 body_statement(new_fn.ast());
181 });
182
183 decl_queue.emplace(&new_fn);
184
185 selected = &new_fn;
186 } else {
187 selected = &inst_fn;
188 }
189 }
190
191 // XXX: STILL Duplicated from function overload handling
192 for (auto [target, expr_arg, compat] : llvm::zip(selected->arg_types(), expr.args, best_overload.compatibilities)) {
193 YUME_ASSERT(compat.valid, "Invalid compatibility after overload already selected?????");
194 if (compat.conv.empty())
195 continue;
196
197 wrap_in_implicit_cast(expr_arg, compat.conv, target);
198 }
199
200 expr.selected_overload = selected;
201 }
202}
203
205 Struct* st = nullptr;
206
207 auto base_type = expr->ensure_ty().without_mut();
208
209 for_all_instantiations(compiler.m_structs, [&](Struct& i) {
210 if (i.self_ty == base_type)
211 st = &i;
212 });
213
214 YUME_ASSERT(st != nullptr, "Cannot duplicate non-struct type " + base_type.name());
215 OverloadSet ctor_overloads{};
216
217 auto ctor_receiver = std::make_unique<ast::SelfType>(expr->token_range());
218 ctor_receiver->val_ty(base_type);
219 auto ctor_args = vector<ast::AnyExpr>{};
220 ctor_args.emplace_back(move(expr));
221 auto ctor_expr = std::make_unique<ast::CtorExpr>(ctor_receiver->token_range(), move(ctor_receiver), move(ctor_args));
222 ctor_expr->val_ty(base_type);
223
224 // XXX: Duplicated from function overload handling
225 resolve_queue();
226 ctor_overloads = all_ctor_overloads_by_type(*st, *ctor_expr);
227 ctor_overloads.args.push_back(ctor_expr->args.front().raw_ptr());
228
229#ifdef YUME_SPEW_OVERLOAD_SELECTION
230 errs() << "\n*** BEGIN CTOR OVERLOAD EVALUATION ***\n";
231 errs() << "Constructors with matching types:\n";
232 ctor_overloads.dump(errs());
233#endif
234
235 ctor_overloads.determine_valid_overloads();
236
237#ifdef YUME_SPEW_OVERLOAD_SELECTION
238 errs() << "\nViable overloads:\n";
239 ctor_overloads.dump(errs(), true);
240#endif
241
242 auto best_overload = ctor_overloads.best_viable_overload();
243
244#ifdef YUME_SPEW_OVERLOAD_SELECTION
245 errs() << "\nSelected overload:\n";
246 best_overload.dump(errs());
247 errs() << "\n*** END CTOR OVERLOAD EVALUATION ***\n\n";
248#endif
249
250 auto& subs = best_overload.subs;
251 auto* selected = best_overload.fn;
252
253 YUME_ASSERT(subs.fully_substituted(), "Constructors cannot be generic"); // TODO(rymiel): revisit?
254
255 // XXX: STILL Duplicated from function overload handling
256 // It is an instantiation of a function template
257 if (!subs.empty()) {
258 // Try to find an already existing instantiation with the same substitutions
259 auto [already_existed, inst_fn] = selected->get_or_create_instantiation(subs);
260 if (!already_existed) {
261 // An existing one wasn't found. We've been given a duplicate of the template's AST but without types
262 // The duplicate will have its types set again according to the substitutions being used.
263 auto& new_fn = inst_fn;
264
265 // The types of the instantiated function must be set immediately (i.e. with in_depth = false)
266 // This is because the implicit cast logic below depends on the direct type being set here and bound type
267 // information doesn't propagate across ImplicitCastExpr...
268 // TODO(rymiel): Find a better solution; such as moving cast logic also into queue?
269 // However, that would require evaluation of the queue very eagerly (i.e. immediately when the function is used,
270 // which kinda defeats the purpose of the queue). So I guess we'll just keep this until I think of a better
271 // solution
272 // TODO(rymiel): find a better solution other than the solution proposed above
273 with_saved_scope([&] {
274 in_depth = false;
275 current_decl = &new_fn;
276 body_statement(new_fn.ast());
277 });
278
279 decl_queue.emplace(&new_fn);
280
281 selected = &new_fn;
282 } else {
283 selected = &inst_fn;
284 }
285 }
286
287 // XXX: STILL Duplicated from function overload handling
288 auto target = selected->arg_types().front();
289 auto& expr_arg = ctor_expr->args.front();
290 auto compat = best_overload.compatibilities.front();
291 YUME_ASSERT(compat.valid, "Invalid compatibility after overload already selected?????");
292 if (!compat.conv.empty())
293 wrap_in_implicit_cast(expr_arg, compat.conv, target);
294 expr = move(ctor_expr);
295
296 return selected;
297}
298
299template <> void TypeWalker::expression(ast::SliceExpr& expr) {
300 for (auto& i : expr.args)
301 body_expression(*i);
302
303 auto& slice_base = *expr.type;
304 expression(slice_base);
305 auto base_type = create_slice_type(convert_type(slice_base));
306
307 // expr.val_ty(&base_type.known_slice());
308 expr.val_ty(base_type);
309}
310
311template <> void TypeWalker::expression(ast::LambdaExpr& expr) {
312 enclosing_scopes.push_back(scope);
313 with_saved_scope([&] {
314 scope.clear();
315 [[maybe_unused]] auto guard = scope.push_scope_guarded();
316
317 auto arg_types = vector<ty::Type>{};
318 auto ret_type = optional<ty::Type>{};
319
320 for (auto& i : expr.args) {
321 expression(i);
322 scope.add(i.name, &i);
323 arg_types.push_back(i.ensure_ty());
324 }
325
326 if (expr.ret.has_value()) {
327 expression(*expr.ret);
328 ret_type = expr.ret->ensure_ty();
329 }
330
331 body_statement(expr.body);
332
333 auto closured_types = vector<ty::Type>();
334 for (const auto& i : closured) {
335 closured_types.push_back(i.ast->ensure_ty());
336 expr.closured_names.push_back(i.name);
337 expr.closured_nodes.push_back(i.ast);
338 }
339
340 expr.val_ty(compiler.m_types.find_or_create_fn_type(arg_types, ret_type, closured_types));
341 });
342 enclosing_scopes.pop_back();
343}
344
345void TypeWalker::direct_call_operator(ast::CallExpr& expr) {
346 YUME_ASSERT(expr.args.size() > 1, "Direct call must have at least 1 argument");
347 auto& base = *expr.args.front();
348 body_expression(base);
349
350 YUME_ASSERT(base.ensure_ty().base_isa<ty::Function>(), "Direct call target must be a function type");
351 const auto* base_ptr_ty = base.ensure_ty().base_cast<ty::Function>();
352
353 for (auto [target, expr_arg] : llvm::zip(base_ptr_ty->args(), llvm::drop_begin(expr.args))) {
354 body_expression(*expr_arg);
355
356 auto compat = expr_arg->ensure_ty().compatibility(target);
357 YUME_ASSERT(compat.valid, "Invalid direct call with incompatible argument types");
358 if (compat.conv.empty())
359 continue;
360
361 wrap_in_implicit_cast(expr_arg, compat.conv, target);
362 }
363
364 expr.val_ty(base_ptr_ty->ret());
365}
366
367template <> void TypeWalker::expression(ast::AssignExpr& expr) {
368 body_expression(*expr.target);
369 body_expression(*expr.value);
370
371 make_implicit_conversion(expr.value, expr.target->ensure_ty().mut_base());
372
373 expr.target->attach_to(expr.value.raw_ptr());
374 expr.attach_to(expr.value.raw_ptr());
375}
376
377template <> void TypeWalker::expression(ast::VarExpr& expr) {
378 if (auto** var = scope.find(expr.name); var != nullptr)
379 return expr.attach_to(*var);
380
381 // If we're inside a lambda body, check if the variable maybe refers to one from an outer scope that can be
382 // included in the closure of the current lambda
383 for (auto& outer_scope : enclosing_scopes) {
384 if (auto** var = outer_scope.find(expr.name); var != nullptr) {
385 // It was found, include it in the current scope, so we don't need to look for it again
386 scope.add_to_front(expr.name, *var);
387 closured.push_back({*var, expr.name});
388 return expr.attach_to(*var);
389 }
390 }
391
392 throw std::runtime_error("Scope doesn't contain variable called "s + expr.name);
393}
394
395template <> void TypeWalker::expression(ast::ConstExpr& expr) {
396 for (const auto& cn : compiler.m_consts)
397 if (cn.referred_to_by(expr))
398 return expr.val_ty(cn.ast().ensure_ty());
399 throw std::runtime_error("Nonexistent constant called "s + expr.name);
400}
401
402static auto find_field_ast(const ty::Struct& st, string_view target_name) -> std::pair<nullable<ast::AnyType*>, int> {
403 nullable<ast::AnyType*> target_type = nullptr;
404 int j = 0;
405 for (auto* field : st.fields()) {
406 if (field->name == target_name) {
407 target_type = &field->type;
408 break;
409 }
410 j++;
411 }
412
413 if (target_type == nullptr)
414 j = -1;
415
416 return {target_type, j};
417}
418
419static auto find_field(const ty::Struct& st, string_view target_name) -> std::pair<optional<ty::Type>, int> {
420 optional<ty::Type> target_type;
421 auto [ast_ptr, j] = find_field_ast(st, target_name);
422 if (ast_ptr != nullptr)
423 target_type = ast_ptr->raw_ptr()->val_ty();
424
425 return {target_type, j};
426}
427
428template <> void TypeWalker::expression(ast::FieldAccessExpr& expr) {
429 optional<ty::Type> type;
430 bool base_is_mut = false;
431
432 if (expr.base.has_value()) {
433 body_expression(*expr.base);
434 type = expr.base->ensure_ty();
435
436 if (type->is_mut()) {
437 type = type->mut_base();
438 base_is_mut = true;
439 };
440 } else {
441 type = current_decl.self_ty();
442 base_is_mut = true;
443 }
444
445 if (!type.has_value())
446 llvm_unreachable("Type must be set in either branch above");
447
448 if (type->is_opaque_self())
449 make_implicit_conversion(expr.base, type->without_opaque());
450
451 const auto* struct_type = type->without_opaque().base_dyn_cast<ty::Struct>();
452
453 if (struct_type == nullptr)
454 throw std::runtime_error("Can't access field of expression with non-struct type");
455
456 auto target_name = expr.field;
457 auto [target_type, target_offset] = find_field(*struct_type, target_name);
458
459 expr.offset = target_offset;
460 expr.val_ty(base_is_mut ? target_type->known_mut() : target_type);
461}
462
463auto TypeWalker::all_fn_overloads_by_name(ast::CallExpr& call) -> OverloadSet {
464 auto fns_by_name = vector<Overload>();
465
466 for (auto& fn : compiler.m_fns)
467 if (fn.name() == call.name)
468 fns_by_name.emplace_back(&fn);
469
470 return OverloadSet{&call, fns_by_name, {}};
471}
472
473auto TypeWalker::all_ctor_overloads_by_type(Struct& st, ast::CtorExpr& call) -> OverloadSet {
474 auto ctors_by_type = vector<Overload>();
475
476 for (auto& ctor : compiler.m_ctors)
477 if (ctor.self_ty && st.self_ty && *ctor.self_ty == st.self_ty->generic_base())
478 ctors_by_type.emplace_back(&ctor);
479
480 return OverloadSet{&call, ctors_by_type, {}};
481}
482
483template <> void TypeWalker::expression(ast::CallExpr& expr) {
484 auto name = expr.name;
485
486 if (name == "->") // TODO(rymiel): Magic value?
487 return direct_call_operator(expr);
488
489 auto overload_set = all_fn_overloads_by_name(expr);
490
491 if (overload_set.empty()) { // HACK
492 resolve_queue(); // HACK
493 overload_set = all_fn_overloads_by_name(expr); // HACK
494 } // HACK
495
496 if (overload_set.empty())
497 throw std::logic_error("No function overload named "s + name);
498
499 if (expr.receiver.has_value())
500 expression(*expr.receiver);
501
502 for (auto& i : expr.args) {
503 body_expression(*i);
504 overload_set.args.push_back(i.raw_ptr());
505 }
506
507#ifdef YUME_SPEW_OVERLOAD_SELECTION
508 errs() << "\n*** BEGIN FN OVERLOAD EVALUATION ***\n";
509 errs() << "Functions with matching names:\n";
510 overload_set.dump(errs());
511#endif
512
513 overload_set.determine_valid_overloads();
514
515#ifdef YUME_SPEW_OVERLOAD_SELECTION
516 errs() << "\nViable overloads:\n";
517 overload_set.dump(errs(), true);
518#endif
519
520 const auto* maybe_best_overload = overload_set.try_best_viable_overload(); // HACK
521 if (maybe_best_overload == nullptr && !decl_queue.empty()) { // HACK
522 resolve_queue(); // HACK
523 return expression(expr); // HACK
524 } // HACK
525 Overload best_overload = overload_set.best_viable_overload();
526
527#ifdef YUME_SPEW_OVERLOAD_SELECTION
528 errs() << "\nSelected overload:\n";
529 best_overload.dump(errs());
530 errs() << "\n*** END FN OVERLOAD EVALUATION ***\n\n";
531#endif
532
533 auto& subs = best_overload.subs;
534 auto* selected = best_overload.fn;
535
536 // It is an instantiation of a function template
537 if (!subs.empty()) {
538 // Try to find an already existing instantiation with the same substitutions
539 auto [already_existed, inst_fn] = selected->get_or_create_instantiation(subs);
540 if (!already_existed) {
541 // An existing one wasn't found. We've been given a duplicate of the template's AST but without types
542 // The duplicate will have its types set again according to the substitutions being used.
543 auto& new_fn = inst_fn;
544
545 // The types of the instantiated function must be set immediately (i.e. with in_depth = false)
546 // This is because the implicit cast logic below depends on the direct type being set here and bound type
547 // information doesn't propagate across ImplicitCastExpr...
548 // TODO(rymiel): Find a better solution; such as moving cast logic also into queue?
549 // However, that would require evaluation of the queue very eagerly (i.e. immediately when the function is used,
550 // which kinda defeats the purpose of the queue). So I guess we'll just keep this until I think of a better
551 // solution
552 // TODO(rymiel): find a better solution other than the solution proposed above
553 with_saved_scope([&] {
554 in_depth = false;
555 current_decl = &new_fn;
556 body_statement(new_fn.ast());
557 });
558
559 decl_queue.emplace(&new_fn);
560
561 selected = &new_fn;
562 } else {
563 selected = &inst_fn;
564 }
565 }
566
567 for (auto [target, expr_arg, compat] : llvm::zip(selected->arg_types(), expr.args, best_overload.compatibilities)) {
568 YUME_ASSERT(compat.valid, "Invalid compatibility after overload already selected?????");
569 if (compat.conv.empty())
570 continue;
571
572 wrap_in_implicit_cast(expr_arg, compat.conv, target);
573 }
574
575 // Find excess variadic arguments. Logic will probably change later, but for now, always pass by value
576 // TODO(rymiel): revisit
577 for (const auto& expr_arg : llvm::enumerate(expr.args)) {
578 if (expr_arg.index() >= selected->arg_count() && expr_arg.value()->ensure_ty().is_mut()) {
579 auto target_type = expr_arg.value()->ensure_ty().mut_base();
580 wrap_in_implicit_cast(expr_arg.value(), ty::Conv{.dereference = true}, target_type);
581 }
582 }
583
584 if (selected->ret().has_value())
585 expr.attach_to(&selected->ast());
586
587 expr.selected_overload = selected;
588}
589
590template <> void TypeWalker::expression(ast::BinaryLogicExpr& expr) {
591 // TODO(rymiel): expand this to apply more than just bools
592 ty::Type bool_ty = compiler.m_types.bool_type;
593 body_expression(*expr.lhs);
594 body_expression(*expr.rhs);
595 YUME_ASSERT(expr.lhs->ensure_ty() == bool_ty, "BinaryLogicExpr lhs must be boolean");
596 YUME_ASSERT(expr.rhs->ensure_ty() == bool_ty, "BinaryLogicExpr rhs must be boolean");
597 expr.val_ty(bool_ty);
598}
599
600template <> void TypeWalker::expression(ast::TypeExpr& expr) {
601 expression(*expr.type);
602 ty::Type base_type = expr.type->ensure_ty();
603 YUME_ASSERT(base_type.is_unqualified(), "Type expression must be unqualified"); // TODO(rymiel): revisit
604 expr.val_ty(base_type.known_meta());
605}
606
607template <> void TypeWalker::statement(ast::Compound& stat) {
608 [[maybe_unused]] auto guard = scope.push_scope_guarded();
609 for (auto& i : stat)
610 body_statement(*i);
611}
612
613template <> void TypeWalker::statement(ast::StructDecl& stat) {
614 for (auto& type_arg : stat.type_args)
615 if (type_arg.type.has_value())
616 expression(*type_arg.type);
617
618 // This decl still has unsubstituted generics, can't instantiate its body
620 return;
621
622 for (auto& i : stat.fields)
623 expression(i);
624
625 if (stat.implements)
626 expression(*stat.implements);
627}
628
629template <> void TypeWalker::statement(ast::FnDecl& stat) {
630 scope.clear();
631 [[maybe_unused]] auto guard = scope.push_scope_guarded();
632
633 auto args = vector<ty::Type>();
634 auto ret = optional<ty::Type>();
635
636 for (auto& i : stat.args) {
637 expression(i);
638 scope.add(i.name, &i);
639 args.push_back(i.ensure_ty());
640 }
641
642 if (stat.ret.has_value()) {
643 expression(*stat.ret);
644 stat.attach_to(stat.ret.raw_ptr());
645 ret = stat.ret->ensure_ty();
646 }
647
648 std::get<Fn*>(current_decl)->fn_ty = compiler.m_types.find_or_create_fn_ptr_type(args, ret, stat.varargs());
649
650 // This decl still has unsubstituted generics, can't instantiate its body
652 return;
653
654 if (in_depth && std::holds_alternative<ast::Compound>(stat.body))
655 statement(get<ast::Compound>(stat.body));
656
657 YUME_ASSERT(scope.size() == 1, "End of function should end with only the function scope remaining");
658}
659
660template <> void TypeWalker::statement(ast::CtorDecl& stat) {
661 scope.clear();
662 [[maybe_unused]] auto guard = scope.push_scope_guarded();
663
664 const auto* struct_type = current_decl.self_ty()->without_mut().base_dyn_cast<ty::Struct>();
665 auto args = vector<ty::Type>();
666
667 if (struct_type == nullptr)
668 throw std::runtime_error("Can't define constructor of non-struct type");
669
670 stat.val_ty(struct_type);
671 for (auto& i : stat.args) {
672 expression(i);
673 scope.add(i.name, &i);
674 args.push_back(i.ensure_ty());
675 }
676
677 for (auto& i : stat.args) {
678 expression(i);
679 scope.add(i.name, &i);
680 }
681
682 std::get<Fn*>(current_decl)->fn_ty = compiler.m_types.find_or_create_fn_ptr_type(args, current_decl.self_ty());
683
684 // This decl still has unsubstituted generics, can't instantiate its body
686 return;
687
688 if (in_depth)
689 statement(stat.body);
690
691 YUME_ASSERT(scope.size() == 1, "End of function should end with only the function scope remaining");
692}
693
694template <> void TypeWalker::statement(ast::ReturnStmt& stat) {
695 if (stat.expr.has_value()) {
696 body_expression(*stat.expr);
697 // If we're returning a local variable, mark that it will leave the scope and should not be destructed yet.
698 if (auto* var_expr = dyn_cast<ast::VarExpr>(stat.expr.raw_ptr())) {
699 if (auto** in_scope = scope.find(var_expr->name); in_scope != nullptr) {
700 if (auto* var_decl = dyn_cast<ast::VarDecl>(*in_scope))
701 stat.extends_lifetime = var_decl;
702 }
703 }
704
706 current_decl.ast()->attach_to(stat.expr.raw_ptr());
707 // TODO(rymiel): Once return type deduction exists, make sure to not return `mut` unless there is an _explicit_ type
708 // annotation saying so
709 }
710}
711
712template <> void TypeWalker::statement(ast::VarDecl& stat) {
713 body_expression(*stat.init);
714 if (stat.type.has_value()) {
715 expression(*stat.type);
716 make_implicit_conversion(stat.init, stat.type->val_ty());
717 stat.init->attach_to(stat.type.raw_ptr());
718 } else {
719 // This does a "decay" of sorts. If an explicit type isn't provided, and the initializer returns a mutable
720 // reference, the variable is initialized from a value instead of a reference. Note that the local variable itself
721 // becomes a reference again, but usually as a copy of the initializer.
722 // TODO(rymiel): Add a way to bypass this decay, i.e. by using `let mut`.
723 make_implicit_conversion(stat.init, stat.init->ensure_ty().without_mut());
724 }
725
726 stat.val_ty(stat.init->ensure_ty().known_mut());
727 scope.add(stat.name, &stat);
728}
729
730template <> void TypeWalker::statement(ast::ConstDecl& stat) {
731 expression(*stat.type);
732 if (in_depth) {
733 body_expression(*stat.init);
734 // TODO(rymiel): Perform literal casts
735 make_implicit_conversion(stat.init, stat.type->val_ty());
736 stat.init->attach_to(stat.type.raw_ptr());
737 }
738 stat.val_ty(stat.type->ensure_ty());
739}
740
741template <> void TypeWalker::statement(ast::IfStmt& stat) {
742 for (auto& i : stat.clauses) {
743 body_expression(*i.cond);
744 statement(i.body);
745 }
746 auto& else_clause = stat.else_clause;
747 if (else_clause)
748 statement(*else_clause);
749}
750
751template <> void TypeWalker::statement(ast::WhileStmt& stat) {
752 body_expression(*stat.cond);
753 statement(stat.body);
754}
755
757 const ASTStackTrace guard("Semantic: "s + stat.kind_name() + " statement", stat);
758 return CRTPWalker::body_statement(stat);
759}
761 const ASTStackTrace guard("Semantic: "s + expr.kind_name() + " expression", expr);
762 return CRTPWalker::body_expression(expr);
763}
764
765auto TypeWalker::get_or_declare_instantiation(Struct* struct_obj, Substitutions subs) -> ty::Type {
766 auto [already_existed, inst_struct] = struct_obj->get_or_create_instantiation(subs);
767
768 if (!already_existed) {
769 auto& new_st = inst_struct;
770
771 with_saved_scope([&] {
772 in_depth = false;
773 current_decl = &new_st;
774 body_statement(new_st.st_ast);
775 });
776
777 if (compiler.create_struct(new_st))
778 decl_queue.emplace(&new_st);
779 }
780
781 return *inst_struct.self_ty;
782}
783
784auto TypeWalker::create_slice_type(const ty::Type& base_type) -> ty::Type {
785 YUME_ASSERT(compiler.m_slice_struct != nullptr, "Can't create slice type if a slice type was never defined");
786 Struct* struct_obj = compiler.m_slice_struct;
787 Substitutions subs = struct_obj->subs;
788 subs.associate(*subs.all_keys().at(0), {base_type});
789 return get_or_declare_instantiation(struct_obj, subs);
790}
791
792auto TypeWalker::convert_type(ast::Type& ast_type) -> ty::Type {
793 auto parent = current_decl.self_ty();
794 const auto* context = current_decl.subs();
795
796 if (const auto* simple_type = dyn_cast<ast::SimpleType>(&ast_type)) {
797 auto name = simple_type->name;
798 if (context != nullptr && !context->empty()) {
799 const auto* generic = context->mapping_ref_or_null({name});
800 if (generic != nullptr && generic->holds_type())
801 return generic->as_type();
802 if (generic != nullptr && generic->unassigned())
803 return context->get_generic_fallback(name);
804 }
805 auto val = compiler.m_types.known.find(name);
806 if (val != compiler.m_types.known.end())
807 return val->second.get();
808 } else if (auto* qual_type = dyn_cast<ast::QualType>(&ast_type)) {
809 auto qualifier = qual_type->qualifier;
810 return convert_type(*qual_type->base).known_qual(qualifier);
811 } else if (isa<ast::SelfType>(ast_type)) {
812 if (parent) {
813 if (current_decl.opaque_self())
814 return {parent->known_opaque().base(), parent->is_mut(), parent->is_ref()};
815 return {parent->base(), parent->is_mut(), parent->is_ref()}; // TODO(rymiel): Isn't this just `parent`
816 }
817 } else if (auto* proxy_type = dyn_cast<ast::ProxyType>(&ast_type)) {
818 if (parent) {
819 if (const auto* parent_struct = parent->base_dyn_cast<ty::Struct>()) {
820 auto [target_type, target_offset] = find_field_ast(*parent_struct, proxy_type->field);
821 if (target_type != nullptr)
822 return convert_type(*target_type->raw_ptr());
823 throw std::runtime_error("Proxy type doesn't refer to a valid field?");
824 }
825 }
826 } else if (auto* fn_type = dyn_cast<ast::FunctionType>(&ast_type)) {
827 auto ret = optional<ty::Type>{};
828 auto args = vector<ty::Type>{};
829 if (auto& ast_ret = fn_type->ret; ast_ret.has_value())
830 ret = convert_type(*ast_ret);
831
832 for (auto& ast_arg : fn_type->args)
833 args.push_back(convert_type(*ast_arg));
834
835 if (fn_type->fn_ptr)
836 return compiler.m_types.find_or_create_fn_ptr_type(args, ret);
837 return compiler.m_types.find_or_create_fn_type(args, ret, {});
838 } else if (auto* templated = dyn_cast<ast::TemplatedType>(&ast_type)) {
839 auto& template_base = *templated->base;
840 expression(template_base);
841 auto base_type = convert_type(template_base);
842 auto* struct_obj = base_type.base_cast<ty::Struct>()->decl();
843
844 if (struct_obj == nullptr)
845 throw std::logic_error("Can't add template arguments to non-struct types");
846
847 for (auto& i : templated->type_args)
848 i.visit([&](ast::AnyType& v) { expression(*v); }, [&](ast::AnyExpr& v) { body_expression(*v); });
849
850 Substitutions gen_base = struct_obj->get_subs();
851 size_t i = 0;
852 for (const auto& [gen, gen_sub] : llvm::zip(gen_base.all_keys(), templated->type_args)) {
853 if (gen->holds_type()) {
854 gen_base.associate(*gen, {gen_sub.as_type()->ensure_ty()});
855 } else {
856 auto* expr = gen_sub.as_expr().raw_ptr();
857 auto expected_type = struct_obj->st_ast.type_args.at(i).type->ensure_ty();
858 auto compat = expr->ensure_ty().compatibility(expected_type);
859 YUME_ASSERT(compat.valid, "Non-type generic argument type must match or be implicitly convertible (got `" +
860 expr->ensure_ty().name() + "', expected `" + expected_type.name() + "')");
861
862 gen_base.associate(*gen, {expr});
863 }
864 ++i;
865 }
866
867 // YUME_ASSERT(gen_base.fully_substituted(), "Can't convert templated type which isn't fully substituted: "s +
868 // template_base.describe() + " (" + ast_type.describe() + ")");
869
870 return get_or_declare_instantiation(struct_obj, gen_base);
871 }
872
873 throw std::runtime_error("Cannot convert AST type to actual type! "s + ast_type.kind_name() + " (" +
874 ast_type.describe() + ")");
875}
876
878 while (!decl_queue.empty()) {
879 auto next = decl_queue.front();
880 decl_queue.pop();
881
882 with_saved_scope([&] {
883 next.visit([](std::monostate /* absent */) {}, //
884 [](Const* /* cn */) {},
885 [&](Fn* fn) {
886 in_depth = true;
887 compiler.declare(*fn);
888 },
889 [](Struct* /* st */) {});
890 });
891 }
892}
893} // namespace yume::semantic
auto declare(Fn &) -> llvm::Function *
Declare a function/constructor in bytecode, or get an existing declaration.
Definition: compiler.cpp:544
auto add(std::string_view key, T object) noexcept
auto push_scope_guarded() noexcept -> ScopeContainerGuard< T >
auto find(std::string_view key) const noexcept -> nullable< const T * >
auto add_to_front(std::string_view key, T object) noexcept
auto size() noexcept -> size_t
void clear() noexcept
void attach_to(nonnull< AST * > other)
Make the type of this node depend on the type of other.
Definition: ast.hpp:266
auto val_ty() const noexcept -> optional< ty::Type >
Definition: ast.hpp:253
auto kind_name() const -> string
Human-readable string representation of the Kind of this node.
Definition: ast.hpp:274
auto ensure_ty() const -> ty::Type
Definition: ast.hpp:254
auto raw_ptr() const -> const T *
Definition: ast.hpp:200
Expressions have an associated value and type.
Definition: ast.hpp:438
auto has_value() const -> bool
Definition: ast.hpp:172
auto raw_ptr() const -> const T *
Definition: ast.hpp:173
Statements make up most things in source code.
Definition: ast.hpp:297
A type annotation. This (ast::Type) is distinct from the actual type of a value (ty::Type).
Definition: ast.hpp:312
A function pointer type.
Definition: type.hpp:129
An user-defined struct type with associated fields.
Definition: type.hpp:78
A "qualified" type, with a non-stackable qualifier, i.e. mut.
Definition: type_base.hpp:66
auto is_unqualified() const noexcept -> bool
Definition: type_base.hpp:116
auto known_meta() const -> Type
Definition: type_base.hpp:104
AnyBase< Expr > AnyExpr
Definition: ast.hpp:448
AnyBase< Type > AnyType
Definition: ast.hpp:322
void make_implicit_conversion(ast::OptionalExpr &expr, optional< ty::Type > target_ty)
Definition: type_walker.cpp:55
static auto for_all_instantiations(std::deque< Struct > &structs, std::invocable< Struct & > auto fn)
Definition: type_walker.cpp:99
auto try_implicit_conversion(ast::OptionalExpr &expr, optional< ty::Type > target_ty) -> bool
Definition: type_walker.cpp:38
void wrap_in_implicit_cast(ast::OptionalExpr &expr, ty::Conv conv, optional< ty::Type > target_type)
Definition: type_walker.cpp:32
static auto find_field(const ty::Struct &st, string_view target_name) -> std::pair< optional< ty::Type >, int >
static auto find_field_ast(const ty::Struct &st, string_view target_name) -> std::pair< nullable< ast::AnyType * >, int >
T nullable
Definition: util.hpp:72
auto body_statement(ast::Stmt &stat, auto &&... args)
Definition: crtp_walker.hpp:26
auto body_expression(ast::Expr &expr, auto &&... args)
Definition: crtp_walker.hpp:42
A constant declaration in the compiler.
Definition: vals.hpp:174
auto fully_substituted() const noexcept -> bool
Definition: vals.hpp:212
auto ast() const noexcept -> const ast::AST *
Definition: vals.hpp:238
auto self_ty() const noexcept -> optional< ty::Type >
Definition: vals.hpp:248
A function declaration in the compiler.
Definition: vals.hpp:52
auto get_or_create_instantiation(Substitutions &subs) noexcept -> std::pair< bool, Fn & >
Definition: vals.cpp:36
A struct declaration in the compiler.
Definition: vals.hpp:136
auto name() const noexcept -> string
Definition: vals.cpp:68
constexpr auto int8() -> IntTypePair
Definition: type_holder.hpp:28
constexpr auto int64() -> IntTypePair
Definition: type_holder.hpp:31
ty::Int * bool_type
Definition: type_holder.hpp:18
auto find_or_create_fn_ptr_type(const vector< ty::Type > &args, optional< ty::Type > ret, bool c_varargs=false) -> ty::Function *
Definition: type_holder.cpp:53
auto find_or_create_fn_type(const vector< ty::Type > &args, optional< ty::Type > ret, const vector< ty::Type > &closure) -> ty::Function *
Definition: type_holder.cpp:43
constexpr auto int32() -> IntTypePair
Definition: type_holder.hpp:30
An assignment (=).
Definition: ast.hpp:652
A logical operator such as || or &&. Since these aren't overloadable, they have their own AST node.
Definition: ast.hpp:581
Bool literals (true or false).
Definition: ast.hpp:508
A function call or operator.
Definition: ast.hpp:562
OptionalType receiver
Definition: ast.hpp:565
vector< AnyExpr > args
Definition: ast.hpp:566
Fn * selected_overload
During semantic analysis, the TypeWalker performs overload selection and saves the function declarati...
Definition: ast.hpp:569
Char literals.
Definition: ast.hpp:495
A statement consisting of multiple other statements, i.e. the body of a function.
Definition: ast.hpp:712
A declaration of a constant (const).
Definition: ast.hpp:888
A constant. Currently global.
Definition: ast.hpp:547
A declaration of a custom constructor (def :new).
Definition: ast.hpp:832
Compound body
Definition: ast.hpp:835
vector< TypeName > args
Definition: ast.hpp:834
A construction of a struct or cast of a primitive.
Definition: ast.hpp:597
vector< AnyExpr > args
Definition: ast.hpp:600
AnyType type
Definition: ast.hpp:599
Fn * selected_overload
During semantic analysis, the TypeWalker performs overload selection and saves the constructor declar...
Definition: ast.hpp:603
Direct access of a field of a struct (::).
Definition: ast.hpp:666
OptionalExpr base
Definition: ast.hpp:668
A declaration of a function (def).
Definition: ast.hpp:762
Body body
If this function declaration refers to a primitive, this field is a string representing the name of t...
Definition: ast.hpp:786
vector< TypeName > args
Definition: ast.hpp:780
OptionalType ret
Definition: ast.hpp:782
An if statement (if), with one or more IfClauses, and optionally an else clause.
Definition: ast.hpp:930
optional< Compound > else_clause
Definition: ast.hpp:933
vector< IfClause > clauses
Definition: ast.hpp:932
Represents an implicit cast to a different type, performed during semantic analysis.
Definition: ast.hpp:685
A local definition of an anonymous function.
Definition: ast.hpp:729
OptionalType ret
Definition: ast.hpp:732
vector< TypeName > args
Definition: ast.hpp:731
vector< string > closured_names
Definition: ast.hpp:735
vector< AST * > closured_nodes
Definition: ast.hpp:736
Number literals.
Definition: ast.hpp:482
Return from a function body.
Definition: ast.hpp:944
VarDecl * extends_lifetime
Definition: ast.hpp:947
OptionalExpr expr
Definition: ast.hpp:946
A slice literal, i.e. an array.
Definition: ast.hpp:632
vector< AnyExpr > args
Definition: ast.hpp:635
String literals.
Definition: ast.hpp:521
A declaration of a struct (struct) or an interface (interface).
Definition: ast.hpp:847
vector< GenericParam > type_args
Definition: ast.hpp:851
OptionalType implements
Definition: ast.hpp:853
vector< TypeName > fields
Definition: ast.hpp:850
Represents a reference to a type.
Definition: ast.hpp:700
AnyType type
Definition: ast.hpp:702
A pair of a Type and an identifier, i.e. a parameter name.
Definition: ast.hpp:396
AnyType type
Definition: ast.hpp:397
A declaration of a local variable (let).
Definition: ast.hpp:872
AnyExpr init
Definition: ast.hpp:876
OptionalType type
Definition: ast.hpp:875
A variable, i.e. just an identifier.
Definition: ast.hpp:534
A while loop (while).
Definition: ast.hpp:904
Compound body
Definition: ast.hpp:907
vector< ty::Compat > compatibilities
Definition: overload.hpp:24
Substitutions subs
Definition: overload.hpp:25
void dump(llvm::raw_ostream &stream) const
Definition: overload.cpp:47
auto make_dup(ast::AnyExpr &expr) -> Fn *
std::queue< DeclLike > decl_queue
Definition: type_walker.hpp:49
void body_statement(ast::Stmt &)
void body_expression(ast::Expr &)
vector< ASTWithName > closured
Definition: type_walker.hpp:47
vector< scope_t > enclosing_scopes
Definition: type_walker.hpp:46
bool in_depth
Whether or not to compile the bodies of methods. Initially, on the parameter types of methods are tra...
Definition: type_walker.hpp:53
#define YUME_ASSERT(assertion, message)
Definition: util.hpp:81