GCC Code Coverage Report


Directory: src/
File: src/frontend/function-scope.cpp
Date: 2025-10-24 11:14:59
Exec Total Coverage
Lines: 378 406 93.1%
Functions: 67 69 97.1%
Branches: 71 88 80.7%

Line Branch Exec Source
1 /* Copyright 2025 Jonathan S. Arney
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * https://github.com/jarney/gyoji/blob/master/LICENSE
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <gyoji-frontend.hpp>
17 #include <gyoji-frontend/function-scope.hpp>
18 #include <sstream>
19
20 using namespace Gyoji::frontend::lowering;
21
22 ///////////////////////////////////////////////////
23 // Graph-theoretic utilities just to walk graphs
24 // and find intersections, differences.
25 ///////////////////////////////////////////////////
26 24 static void walk_priors(
27 const std::map<size_t, size_t> & backward_edges,
28 size_t start_point,
29 std::map<size_t, size_t> & priors)
30 {
31 24 size_t p = start_point;
32 while (p >= 0) {
33 114 priors.insert(std::pair(p,p));
34 114 const auto & it = backward_edges.find(p);
35
2/2
✓ Branch 2 taken 24 times.
✓ Branch 3 taken 90 times.
114 if (it == backward_edges.end()) {
36 24 break;
37 }
38 90 p = it->second;
39 90 }
40 24 }
41
42 static void
43 12 evaluate_scope_changes(
44 const std::vector<const ScopeOperation*> & flat,
45 const std::map<size_t, size_t> & prior_to_goto,
46 const std::map<size_t, size_t> & prior_to_label,
47 std::vector<const ScopeOperation*> & skipped_initializations,
48 std::vector<const ScopeOperation*> & unwind_variables
49 )
50 {
51
2/2
✓ Branch 5 taken 56 times.
✓ Branch 6 taken 12 times.
68 for (const auto & lp : prior_to_label) {
52
2/2
✓ Branch 3 taken 20 times.
✓ Branch 4 taken 36 times.
56 if (prior_to_goto.find(lp.first) == prior_to_goto.end()) {
53 20 const ScopeOperation *op = flat.at(lp.first);
54
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 10 times.
20 if (op->get_type() == ScopeOperation::VAR_DECL) {
55 10 skipped_initializations.push_back(op);
56 }
57 }
58 }
59
2/2
✓ Branch 5 taken 58 times.
✓ Branch 6 taken 12 times.
70 for (const auto & gp : prior_to_goto) {
60
2/2
✓ Branch 3 taken 22 times.
✓ Branch 4 taken 36 times.
58 if (prior_to_label.find(gp.first) == prior_to_label.end()) {
61 22 const ScopeOperation *op = flat.at(gp.first);
62
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 10 times.
22 if (op->get_type() == ScopeOperation::VAR_DECL) {
63 12 unwind_variables.push_back(op);
64 }
65 }
66 }
67 // We reverse the list because
68 // we should unwind the variables
69 // in the reverse order they were declared in.
70 12 std::reverse(unwind_variables.begin(), unwind_variables.end());
71 12 }
72
73 ///////////////////////////////////////////////////
74 // ScopeOperation
75 ///////////////////////////////////////////////////
76 954 ScopeOperation::ScopeOperation(
77 ScopeOperationType _type,
78 const Gyoji::context::SourceReference & _source_ref
79 954 )
80 954 : type(_type)
81 954 , source_ref(_source_ref)
82 954 {}
83
84 954 ScopeOperation::~ScopeOperation()
85 954 {}
86
87 const ScopeOperation::ScopeOperationType &
88 90 ScopeOperation::get_type() const
89 90 { return type; }
90
91 const std::string &
92 18 ScopeOperation::get_label_name() const
93 18 { return label_name; }
94
95 const std::string &
96 38 ScopeOperation::get_goto_label() const
97 38 { return goto_label; }
98
99 const FunctionPoint &
100 4 ScopeOperation::get_goto_point() const
101 4 { return *goto_point; }
102
103 const std::string &
104 50 ScopeOperation::get_variable_name() const
105 50 { return variable_name; }
106
107 const Gyoji::mir::Type *
108 8 ScopeOperation::get_variable_type() const
109 8 { return variable_type; }
110
111 Gyoji::owned<ScopeOperation>
112 860 ScopeOperation::create_variable(
113 std::string _variable_name,
114 const Gyoji::mir::Type *_variable_type,
115 const Gyoji::context::SourceReference & _source_ref
116 )
117 {
118 860 auto op = Gyoji::owned<ScopeOperation>(new ScopeOperation(ScopeOperation::VAR_DECL, _source_ref));
119 860 op->variable_name = _variable_name;
120 860 op->variable_type = _variable_type;
121 860 return op;
122 }
123 Gyoji::owned<ScopeOperation>
124 10 ScopeOperation::create_label(
125 std::string _label_name,
126 const Gyoji::context::SourceReference & _source_ref
127 )
128 {
129 10 auto op = Gyoji::owned<ScopeOperation>(new ScopeOperation(ScopeOperation::LABEL_DEFINITION, _source_ref));
130 10 op->label_name = _label_name;
131 10 return op;
132 }
133
134 Gyoji::owned<ScopeOperation>
135 12 ScopeOperation::create_goto(
136 std::string _goto_label,
137 Gyoji::owned<FunctionPoint> _goto_point,
138 const Gyoji::context::SourceReference & _source_ref
139 )
140 {
141 12 auto op = Gyoji::owned<ScopeOperation>(new ScopeOperation(ScopeOperation::GOTO_DEFINITION, _source_ref));
142 12 op->goto_label = _goto_label;
143 12 op->goto_point = std::move(_goto_point);
144 12 return op;
145 }
146
147 Gyoji::owned<ScopeOperation>
148 72 ScopeOperation::create_child(
149 Gyoji::owned<Scope> _child,
150 const Gyoji::context::SourceReference & _source_ref
151 )
152 {
153 72 auto op = Gyoji::owned<ScopeOperation>(new ScopeOperation(ScopeOperation::CHILD_SCOPE, _source_ref));
154 72 op->child = std::move(_child);
155 72 return op;
156 }
157
158 const Gyoji::context::SourceReference &
159 16 ScopeOperation::get_source_ref() const
160 16 { return source_ref; }
161
162 ///////////////////////////////////////////////////
163 // Scope
164 ///////////////////////////////////////////////////
165 344 Scope::Scope(bool _is_unsafe)
166 344 : parent(nullptr)
167 344 , scope_is_loop(false)
168 344 , scope_is_unsafe(_is_unsafe)
169 344 , loop_break_blockid(0)
170 344 , loop_continue_blockid(0)
171 344 {}
172
173 4 Scope::Scope(
174 bool _is_unsafe,
175 bool _is_loop,
176 size_t _loop_break_blockid,
177 size_t _loop_continue_blockid
178 4 )
179 4 : parent(nullptr)
180 4 , scope_is_loop(_is_loop)
181 4 , scope_is_unsafe(_is_unsafe)
182 4 , loop_break_blockid(_loop_break_blockid)
183 4 , loop_continue_blockid(_loop_continue_blockid)
184 4 {}
185
186 348 Scope::~Scope()
187 348 {}
188
189 void
190 954 Scope::add_operation(Gyoji::owned<ScopeOperation> op)
191 {
192 954 operations.push_back(std::move(op));
193 954 }
194
195 const LocalVariable *
196 3244 Scope::get_variable(std::string name) const
197 {
198 3244 const auto & it = variables.find(name);
199
2/2
✓ Branch 2 taken 1178 times.
✓ Branch 3 taken 2066 times.
3244 if (it == variables.end()) {
200 1178 return nullptr;
201 }
202 2066 return it->second.get();
203 }
204
205 bool
206 22 Scope::is_loop() const
207 {
208 22 return scope_is_loop;
209 }
210 bool
211 156 Scope::is_unsafe() const
212 {
213 156 return scope_is_unsafe;
214 }
215 size_t
216 2 Scope::get_loop_break_blockid() const
217 2 { return loop_break_blockid; }
218
219 size_t
220 2 Scope::get_loop_continue_blockid() const
221 2 { return loop_continue_blockid; }
222
223 void
224 860 Scope::add_variable(std::string name, const Gyoji::mir::Type *mir_type, const Gyoji::context::SourceReference & source_ref)
225 {
226 860 Gyoji::owned<LocalVariable> local_variable = std::make_unique<LocalVariable>(mir_type, source_ref);
227 860 variables.insert(std::pair(name, std::move(local_variable)));
228 860 variables_in_declaration_order.push_back(name);
229 860 }
230
231 const std::map<std::string, Gyoji::owned<LocalVariable>> &
232 Scope::get_variables() const
233 { return variables; }
234
235 const std::vector<std::string> &
236 338 Scope::get_variables_in_declaration_order() const
237 338 { return variables_in_declaration_order; }
238
239 Scope *
240 1712 Scope::get_parent() const
241 1712 { return parent; }
242
243 void
244 72 Scope::set_parent(Scope *_parent)
245 72 { parent = _parent; }
246
247 ///////////////////////////////////////////////////
248 // ScopeTracker
249 ///////////////////////////////////////////////////
250 276 ScopeTracker::ScopeTracker(
251 bool _root_is_unsafe,
252 276 const Gyoji::context::CompilerContext & _compiler_context)
253 276 : root(std::make_unique<Scope>(_root_is_unsafe))
254 276 , compiler_context(_compiler_context)
255 276 , tracker_prior_point()
256 276 , tracker_backward_edges()
257 552 , tracker_flat()
258 {
259 276 tracker_prior_point.push_back((size_t)-1);
260 276 current = root.get();
261 276 }
262
263 276 ScopeTracker::~ScopeTracker()
264 {
265 276 }
266
267
268 void
269 68 ScopeTracker::scope_push(bool _is_unsafe, const Gyoji::context::SourceReference & _source_ref)
270 {
271 68 auto child_scope = std::make_unique<Scope>(_is_unsafe);
272 68 Scope *new_current = child_scope.get();
273 68 child_scope->set_parent(current);
274 68 auto child_op = ScopeOperation::create_child(std::move(child_scope), _source_ref);
275 68 current->add_operation(std::move(child_op));
276 68 current = new_current;
277 68 tracker_prior_point.push_back(tracker_prior_point.back());
278 68 }
279
280 void
281 4 ScopeTracker::scope_push_loop(const Gyoji::context::SourceReference & _source_ref, size_t _loop_break_blockid, size_t _loop_continue_blockid)
282 {
283 // Scopes associated with loops cannot directly
284 // be declared as unsafe, so if you want that, you should
285 // do it in the parent scope of the loop.
286 4 auto child_scope = std::make_unique<Scope>(false, true, _loop_break_blockid, _loop_continue_blockid);
287 4 Scope *new_current = child_scope.get();
288 4 child_scope->set_parent(current);
289 4 auto child_op = ScopeOperation::create_child(std::move(child_scope), _source_ref);
290 4 current->add_operation(std::move(child_op));
291 4 current = new_current;
292 4 tracker_prior_point.push_back(tracker_prior_point.back());
293 4 }
294
295
296 void
297 72 ScopeTracker::scope_pop()
298 {
299 72 current = current->get_parent();
300 72 tracker_prior_point.pop_back();
301 72 }
302
303
304 const FunctionLabel *
305 16 ScopeTracker::get_label(std::string name) const
306 {
307 16 const auto & it_forward_declared = labels_forward_declared.find(name);
308
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
16 if (it_forward_declared != labels_forward_declared.end()) {
309 return it_forward_declared->second.get();
310 }
311 16 const auto & it = labels.find(name);
312
2/2
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 2 times.
16 if (it != labels.end()) {
313 14 return it->second.get();
314 }
315 2 return nullptr;
316 }
317
318 void
319 2 ScopeTracker::label_define(
320 std::string label_name,
321 const Gyoji::context::SourceReference & _source_ref
322 )
323 {
324 2 const auto & it_forward_declared = labels_forward_declared.find(label_name);
325
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (it_forward_declared == labels_forward_declared.end()) {
326 fprintf(stderr, "Compiler Bug! We are attempting to resolve the definition of a forward-declared label, but it wasn't forward-declared.\n");
327 exit(1);
328 return;
329 }
330 2 it_forward_declared->second->resolve(_source_ref);
331 2 labels.insert(std::pair(label_name, std::move(it_forward_declared->second)));
332 2 labels_forward_declared.erase(it_forward_declared);
333
334 2 auto op = ScopeOperation::create_label(label_name, _source_ref);
335 2 tracker_label_locations.insert(std::pair(op->get_label_name(), tracker_flat.size()));
336 2 add_flat_op(op.get());
337
338 2 add_operation(std::move(op));
339 2 }
340
341 void
342 8 ScopeTracker::label_define(
343 std::string label_name,
344 size_t label_blockid,
345 const Gyoji::context::SourceReference & _source_ref
346 )
347 {
348 8 Gyoji::owned<FunctionLabel> new_label = std::make_unique<FunctionLabel>(label_blockid);
349 8 new_label->resolve(_source_ref);
350 8 labels.insert(std::pair(label_name, std::move(new_label)));
351
352 8 auto op = ScopeOperation::create_label(label_name, _source_ref);
353 8 tracker_label_locations.insert(std::pair(op->get_label_name(), tracker_flat.size()));
354 8 add_flat_op(op.get());
355 8 add_operation(std::move(op));
356 8 }
357
358 // Use this for 'goto' to say we want a label, but we
359 // don't yet know where it will live, so put it on the
360 // notfound labels list.
361 void
362 2 ScopeTracker::label_declare(std::string label_name, size_t label_blockid)
363 {
364 2 Gyoji::owned<FunctionLabel> new_label = std::make_unique<FunctionLabel>(label_blockid);
365 2 labels_forward_declared.insert(std::pair(label_name, std::move(new_label)));
366 2 }
367
368 void
369 882 ScopeTracker::add_flat_op(const ScopeOperation *op)
370 {
371
2/2
✓ Branch 1 taken 614 times.
✓ Branch 2 taken 268 times.
882 if (tracker_prior_point.back() != (size_t)-1) {
372 614 tracker_backward_edges.insert(std::pair(tracker_flat.size(), tracker_prior_point.back()));
373 }
374 882 tracker_prior_point.back() = tracker_flat.size();
375 882 tracker_flat.push_back(op);
376 882 }
377
378
379 void
380 12 ScopeTracker::add_goto(
381 std::string goto_label,
382 Gyoji::owned<FunctionPoint> goto_point,
383 const Gyoji::context::SourceReference & _source_ref
384 )
385 {
386 // TODO:
387 // Check if this label exists and jump to that
388 // label if it does. If it doesn't, forward declare it.
389 // and put it in the 'forward declared' labels list.
390 12 auto op = ScopeOperation::create_goto(goto_label, std::move(goto_point), _source_ref);
391 12 tracker_goto_labels_at.insert(std::pair(tracker_flat.size(), op->get_goto_label()));
392 12 add_flat_op(op.get());
393 12 add_operation(std::move(op));
394 12 }
395
396 const LocalVariable *
397 3012 ScopeTracker::get_variable(std::string variable_name) const
398 {
399 // Walk up from the current scope up to the root and
400 // see if this variable is defined anywhere.
401 3012 const Scope *s = current;
402
2/2
✓ Branch 0 taken 3244 times.
✓ Branch 1 taken 946 times.
4190 while (s) {
403 3244 const LocalVariable *existing_local_variable = s->get_variable(variable_name);
404
2/2
✓ Branch 0 taken 2066 times.
✓ Branch 1 taken 1178 times.
3244 if (existing_local_variable != nullptr) {
405 2066 return existing_local_variable;
406 }
407 1178 s = s->get_parent();
408 }
409 946 return nullptr;
410 }
411
412 std::vector<std::string>
413 284 ScopeTracker::get_variables_to_unwind_for_root() const
414 {
415 284 std::vector<std::string> variables_to_unwind;
416 284 const Scope *s = current;
417
2/2
✓ Branch 0 taken 302 times.
✓ Branch 1 taken 284 times.
586 while (s) {
418 // Reverse order because we want to
419 // call destructors in the reverse order of declaration.
420 302 auto & vars = s->get_variables_in_declaration_order();
421
2/2
✓ Branch 4 taken 884 times.
✓ Branch 5 taken 302 times.
1186 for (std::vector<std::string>::const_reverse_iterator it = vars.rbegin(); it != vars.rend(); ++it) {
422 884 variables_to_unwind.push_back(*it);
423 }
424 302 s = s->get_parent();
425 }
426 284 return variables_to_unwind;
427 }
428
429 std::vector<std::string>
430 36 ScopeTracker::get_variables_to_unwind_for_scope() const
431 {
432 36 std::vector<std::string> variables_to_unwind;
433 36 const Scope *s = current;
434 36 const auto & vars = s->get_variables_in_declaration_order();
435
2/2
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 36 times.
48 for (std::vector<std::string>::const_reverse_iterator it = vars.rbegin(); it != vars.rend(); ++it) {
436 12 variables_to_unwind.push_back(*it);
437 }
438 36 return variables_to_unwind;
439 }
440
441 std::vector<std::string>
442 2 ScopeTracker::get_variables_to_unwind_for_break() const
443 {
444 2 std::vector<std::string> variables_to_unwind;
445 2 const Scope *s = current;
446
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
2 while (s && s->is_loop()) {
447 const auto & vars = s->get_variables_in_declaration_order();
448 for (std::vector<std::string>::const_reverse_iterator it = vars.rbegin(); it != vars.rend(); ++it) {
449 variables_to_unwind.push_back(*it);
450 }
451 s = s->get_parent();
452 }
453 2 return variables_to_unwind;
454
455 }
456
457 const Scope *
458 ScopeTracker::get_current() const
459 { return current; }
460
461
462 bool
463 90 ScopeTracker::is_unsafe() const
464 {
465 90 Scope* cur = current;
466
2/2
✓ Branch 0 taken 156 times.
✓ Branch 1 taken 82 times.
238 while (cur) {
467
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 148 times.
156 if (cur->is_unsafe()) {
468 8 return true;
469 }
470 148 cur = cur->get_parent();
471 }
472 82 return false;
473 }
474
475 bool
476 862 ScopeTracker::add_variable(std::string variable_name, const Gyoji::mir::Type *mir_type, const Gyoji::context::SourceReference & source_ref)
477 {
478 // Walk up from the current scope up to the root and
479 // see if this variable is defined anywhere.
480 862 const LocalVariable *maybe_existing = get_variable(variable_name);
481
482
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 860 times.
862 if (maybe_existing != nullptr) {
483 2 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Duplicate Local Variable.");
484 4 error->add_message(source_ref,
485 4 std::string("Variable with name ") + variable_name + " is already in scope and cannot be duplicated.");
486 4 error->add_message(maybe_existing->get_source_ref(),
487 "First declared here.");
488
489 2 compiler_context
490 2 .get_errors()
491 2 .add_error(std::move(error));
492 2 return false;
493 2 }
494
495
496 // Variable was not declared earlier, so we add it to
497 // the current scope.
498 860 current->add_variable(variable_name, mir_type, source_ref);
499
500 860 auto local_var_op = ScopeOperation::create_variable(variable_name, mir_type, source_ref);
501 860 add_flat_op(local_var_op.get());
502 860 add_operation(std::move(local_var_op));
503
504 860 return true;
505 860 }
506
507
508 // TODO: We should give a better interface
509 // here so we can easily declare locals, labels, gotos.
510 void
511 882 ScopeTracker::add_operation(Gyoji::owned<ScopeOperation> op)
512 {
513 882 current->add_operation(std::move(op));
514 882 }
515
516
517 // Now from this, I can evaluate
518 // what's legal. We only need to add the 'SourceReference'
519 // stuff in order to produce good errors for this.
520 void
521 6 ScopeTracker::dump() const
522 {
523 6 Scope *s = root.get();
524 6 fprintf(stderr, "{\n");
525 6 s->dump(0);
526 6 fprintf(stderr, "}\n");
527
2/2
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 6 times.
54 for (size_t i = 0; i < tracker_flat.size(); i++) {
528 48 const auto & op = tracker_flat.at(i);
529
3/5
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
48 switch (op->get_type()) {
530 30 case ScopeOperation::VAR_DECL:
531 30 fprintf(stderr, "%ld Var decl %s\n", i, op->get_variable_name().c_str());
532 30 break;
533 8 case ScopeOperation::LABEL_DEFINITION:
534 8 fprintf(stderr, "%ld label %s\n", i, op->get_label_name().c_str());
535 8 break;
536 10 case ScopeOperation::GOTO_DEFINITION:
537 10 fprintf(stderr, "%ld goto %s\n", i, op->get_goto_label().c_str());
538 10 break;
539 case ScopeOperation::CHILD_SCOPE:
540 fprintf(stderr, "This should not be here\n");
541 exit(1);
542 }
543 }
544 6 }
545
546 bool
547 4 ScopeTracker::is_in_loop() const
548 {
549 4 const Scope *s = current;
550
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 while (s) {
551
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 6 times.
10 if (s->is_loop()) {
552 4 return true;
553 }
554 6 s = s->get_parent();
555 }
556 return false;
557 }
558
559 size_t
560 2 ScopeTracker::get_loop_break_blockid() const
561 {
562 2 const Scope *s = current;
563
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 while (s) {
564
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 if (s->is_loop()) {
565 2 return s->get_loop_break_blockid();
566 }
567 2 s = s->get_parent();
568 }
569 return 0;
570 }
571
572 size_t
573 2 ScopeTracker::get_loop_continue_blockid() const
574 {
575 2 const Scope *s = current;
576
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 while (s) {
577
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
6 if (s->is_loop()) {
578 2 return s->get_loop_continue_blockid();
579 }
580 4 s = s->get_parent();
581 }
582 return 0;
583 }
584
585 bool
586 276 ScopeTracker::check(
587 std::vector<std::pair<const ScopeOperation*, std::vector<const ScopeOperation*>>> & goto_fixups
588 ) const
589 {
590 276 bool ok = true;
591
2/2
✓ Branch 5 taken 12 times.
✓ Branch 6 taken 276 times.
288 for (const auto & goto_point : tracker_goto_labels_at) {
592 12 const ScopeOperation *goto_operation = tracker_flat.at(goto_point.first);
593 12 const FunctionLabel *function_label = get_label(goto_operation->get_goto_label());
594
3/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 12 times.
12 if (function_label == nullptr || !function_label->is_resolved()) {
595 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Goto for an un-defined label.");
596 error->add_message(goto_operation->get_source_ref(),
597 std::string("Goto label ") + goto_operation->get_goto_label() + " had an undefined destination.");
598 compiler_context
599 .get_errors()
600 .add_error(std::move(error));
601 ok = false;
602 continue;
603 }
604
605 12 std::map<size_t, size_t> prior_to_goto;
606 12 walk_priors(tracker_backward_edges, goto_point.first, prior_to_goto);
607
608 12 std::map<size_t, size_t> prior_to_label;
609 12 const auto it = tracker_label_locations.find(goto_point.second);
610
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 if (it == tracker_label_locations.end()) {
611 fprintf(stderr, "This is a bug, we have a defined label, but no location for it\n");
612 exit(1);
613 }
614 12 size_t label_point = it->second;
615
616 12 walk_priors(tracker_backward_edges, label_point, prior_to_label);
617
618 12 std::vector<const ScopeOperation*> skipped_initializations;
619 12 std::vector<const ScopeOperation*> unwind_variables;
620
621 12 evaluate_scope_changes(tracker_flat, prior_to_goto, prior_to_label, skipped_initializations, unwind_variables);
622
623
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 8 times.
12 if (skipped_initializations.size() > 0) {
624 4 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Goto would skip initialization.");
625 8 error->add_message(goto_operation->get_source_ref(),
626 16 std::string("Goto label ") + goto_operation->get_goto_label() + std::string(" would skip initialization of variables in destination scope."));
627 8 error->add_message(function_label->get_source_ref(),
628 "Label declared here.");
629 8 error->add_message(skipped_initializations.at(0)->get_source_ref(),
630 "Skipped initialization occurs here.");
631 4 compiler_context
632 4 .get_errors()
633 4 .add_error(std::move(error));
634 4 ok = false;
635 4 }
636
637 12 goto_fixups.push_back(std::pair(goto_operation, unwind_variables));
638
2/2
✓ Branch 5 taken 12 times.
✓ Branch 6 taken 12 times.
24 for (const auto & s : unwind_variables) {
639 12 fprintf(stderr, "Unwind %s\n", s->get_variable_name().c_str());
640 }
641 12 }
642 276 return ok;
643 }
644
645 void
646 64 ScopeOperation::dump(int indent) const
647 {
648 64 std::string pad(indent*8, ' ');
649
650
4/5
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
64 switch (type) {
651 30 case ScopeOperation::VAR_DECL:
652 {
653 60 std::string var = pad + std::string("var ") + variable_name;
654 30 fprintf(stderr, "%s\n", var.c_str());
655 30 }
656 30 break;
657 8 case ScopeOperation::LABEL_DEFINITION:
658 {
659 16 std::string var = pad + std::string("label ") + label_name;
660 8 fprintf(stderr, "%s\n", var.c_str());
661 8 }
662 8 break;
663 10 case ScopeOperation::GOTO_DEFINITION:
664 {
665 20 std::string var = pad + std::string("goto ") + goto_label;
666 10 fprintf(stderr, "%s\n", var.c_str());
667 10 }
668 10 break;
669 16 case ScopeOperation::CHILD_SCOPE:
670 {
671 16 std::string open = pad + "{";
672 16 fprintf(stderr, "%s\n", open.c_str());
673 16 child->dump(indent+1);
674 16 std::string close = pad + "}";
675 16 fprintf(stderr, "%s\n", close.c_str());
676 16 }
677 16 break;
678 }
679 64 }
680
681 void
682 22 Scope::dump(int indent) const
683 {
684 22 std::string pad(indent*8, ' ');
685
2/2
✓ Branch 5 taken 64 times.
✓ Branch 6 taken 22 times.
86 for (const auto & op : operations) {
686 64 op->dump(indent+1);
687 }
688 22 }
689
690 ///////////////////////////////////////////
691 860 LocalVariable::LocalVariable(
692 const Gyoji::mir::Type *_type,
693 const Gyoji::context::SourceReference & _source_ref
694 860 )
695 860 : type(_type)
696 860 , source_ref(_source_ref)
697 860 {}
698
699 860 LocalVariable::~LocalVariable()
700 860 {}
701
702 const Gyoji::mir::Type *
703 3232 LocalVariable::get_type() const
704 3232 { return type; }
705
706 const Gyoji::context::SourceReference &
707 2 LocalVariable::get_source_ref() const
708 2 { return source_ref; }
709
710 /////////////////////////////////////
711 // FunctionLabel
712 /////////////////////////////////////
713 10 FunctionLabel::FunctionLabel(
714 size_t _block_id
715 10 )
716 10 : resolved(false)
717 10 , block_id(_block_id)
718 10 , src_ref(nullptr)
719 10 {}
720 10 FunctionLabel::~FunctionLabel()
721 10 {}
722
723 const Gyoji::context::SourceReference &
724 4 FunctionLabel::get_source_ref() const
725 4 { return *src_ref; }
726
727 size_t
728 2 FunctionLabel::get_block() const
729 2 { return block_id; }
730
731 bool
732 12 FunctionLabel::is_resolved() const
733 12 { return resolved; }
734
735 void
736 10 FunctionLabel::resolve(const Gyoji::context::SourceReference & _src_ref)
737 {
738 10 resolved = true;
739 10 src_ref = &_src_ref;
740 10 }
741
742 ////////////////////////////////////////////////
743 // FunctionPoint
744 ////////////////////////////////////////////////
745 12 FunctionPoint::FunctionPoint(size_t _basic_block_id, size_t _location)
746 12 : basic_block_id(_basic_block_id)
747 12 , location(_location)
748 12 {}
749 12 FunctionPoint::~FunctionPoint()
750 12 {}
751
752 size_t
753 2 FunctionPoint::get_basic_block_id() const
754 2 { return basic_block_id; }
755
756 size_t
757 2 FunctionPoint::get_location() const
758 2 { return location; }
759