GCC Code Coverage Report


Directory: src/
File: src/frontend/function-resolver.cpp
Date: 2025-10-15 09:43:47
Exec Total Coverage
Lines: 783 1305 60.0%
Functions: 45 50 90.0%
Branches: 314 579 54.2%

Line Branch Exec Source
1 #include <gyoji-frontend/function-resolver.hpp>
2 #include <gyoji-misc/jstring.hpp>
3 #include <variant>
4 #include <stdio.h>
5
6 using namespace Gyoji::mir;
7 using namespace Gyoji::context;
8 using namespace Gyoji::frontend::tree;
9 using namespace Gyoji::frontend::lowering;
10 using namespace Gyoji::frontend::namespaces;
11
12 32 FunctionResolver::FunctionResolver(
13 Gyoji::context::CompilerContext & _compiler_context,
14 const Gyoji::frontend::ParseResult & _parse_result,
15 Gyoji::mir::MIR & _mir,
16 Gyoji::frontend::lowering::TypeResolver & _type_resolver
17 32 )
18 32 : compiler_context(_compiler_context)
19 32 , parse_result(_parse_result)
20 32 , mir(_mir)
21 32 , type_resolver(_type_resolver)
22 32 {}
23
24 32 FunctionResolver::~FunctionResolver()
25 32 {}
26
27 32 bool FunctionResolver::resolve()
28 {
29 32 return extract_functions(parse_result.get_translation_unit().get_statements());
30 }
31 bool
32 12 FunctionResolver::extract_from_namespace(
33 const FileStatementNamespace & namespace_declaration)
34 {
35 12 const auto & statements = namespace_declaration.get_statement_list().get_statements();
36 12 return extract_functions(statements);
37 }
38
39 bool
40 16 FunctionResolver::extract_from_class_definition(const ClassDefinition & definition)
41 {
42 // TODO: We should extract members as
43 // functions from classes so we can set up method calls.
44 // and prepare resolution of class members as variables.
45 // We'll do this last after all the other operations are
46 // supported because we want to first work with 'normal'
47 // C constructs before diving into the OO aspects.
48 //
49 //fprintf(stderr, "Extracting from class definition, constructors and destructors which are special-case functions.\n");
50 // These must be linked back to their corresponding type definitions
51 // so that we can generate their code.
52 16 return true;
53 }
54
55 bool
56 44 FunctionResolver::extract_functions(const std::vector<Gyoji::owned<FileStatement>> & statements)
57 {
58
2/2
✓ Branch 5 taken 366 times.
✓ Branch 6 taken 44 times.
410 for (const auto & statement : statements) {
59 366 const auto & file_statement = statement->get_statement();
60
2/2
✓ Branch 1 taken 314 times.
✓ Branch 2 taken 52 times.
366 if (std::holds_alternative<Gyoji::owned<FileStatementFunctionDeclaration>>(file_statement)) {
61 // Nothing, no functions can exist here.
62 }
63
2/2
✓ Branch 1 taken 266 times.
✓ Branch 2 taken 48 times.
314 else if (std::holds_alternative<Gyoji::owned<FileStatementFunctionDefinition>>(file_statement)) {
64 // This is the only place that functions can be extracted from.
65 // We make this a separate object because we want convenient
66 // access to certain pieces of context used in resolution.
67 FunctionDefinitionResolver function_def_resolver(
68 compiler_context,
69 266 *std::get<Gyoji::owned<FileStatementFunctionDefinition>>(file_statement),
70 mir,
71 type_resolver
72 532 );
73
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 266 times.
266 if (!function_def_resolver.resolve()) {
74 return false;
75 }
76
1/2
✓ Branch 1 taken 266 times.
✗ Branch 2 not taken.
266 }
77
2/2
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 2 times.
48 else if (std::holds_alternative<Gyoji::owned<FileStatementGlobalDefinition>>(file_statement)) {
78 // TODO:
79 // We want to resolve global variables at this stage, but
80 // for now, let's handle resolution of local and stack variables
81 // before we dive into global resolution.
82 }
83
2/2
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 8 times.
46 else if (std::holds_alternative<Gyoji::owned<ClassDeclaration>>(file_statement)) {
84 // Nothing, no functions can exist here.
85 // Class declarations should already be resolved by the type_resolver earlier.
86 }
87
2/2
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 22 times.
38 else if (std::holds_alternative<Gyoji::owned<ClassDefinition>>(file_statement)) {
88 // Constructors, Destructors, and methods are special cases.
89
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
16 if (!extract_from_class_definition(*std::get<Gyoji::owned<ClassDefinition>>(file_statement))) {
90 return false;
91 }
92 }
93
1/2
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
22 else if (std::holds_alternative<Gyoji::owned<EnumDefinition>>(file_statement)) {
94 // Nothing, no functions can exist here.
95 // Enums should already be resolved by the type_resolver earlier.
96 }
97
2/2
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 6 times.
22 else if (std::holds_alternative<Gyoji::owned<TypeDefinition>>(file_statement)) {
98 // Nothing, no functions can exist here.
99 // Typedefs should already be resolved by the type_resolver earlier.
100 }
101
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 4 times.
16 else if (std::holds_alternative<Gyoji::owned<FileStatementNamespace>>(file_statement)) {
102
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
12 if (!extract_from_namespace(*std::get<Gyoji::owned<FileStatementNamespace>>(file_statement))) {
103 return false;
104 }
105 }
106
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 else if (std::holds_alternative<Gyoji::owned<FileStatementUsing>>(file_statement)) {
107 // Namespace using is largely handled by the parse stage, so we don't
108 // need to do any function resolution here.
109 }
110 else {
111 compiler_context
112 .get_errors()
113 .add_simple_error(statement->get_source_ref(),
114 "Compiler bug! Please report this message(5)",
115 "Unknown statement type in variant, extracting statements from file (compiler bug)"
116 );
117 return false;
118 }
119 }
120 44 return true;
121 }
122
123 ////////////////////////////////////////////////
124 // FunctionDefinitionResolver
125 ////////////////////////////////////////////////
126
127 266 FunctionDefinitionResolver::FunctionDefinitionResolver(
128 Gyoji::context::CompilerContext & _compiler_context,
129 const Gyoji::frontend::tree::FileStatementFunctionDefinition & _function_definition,
130 Gyoji::mir::MIR & _mir,
131 Gyoji::frontend::lowering::TypeResolver & _type_resolver
132 266 )
133 266 : compiler_context(_compiler_context)
134 266 , function_definition(_function_definition)
135 266 , mir(_mir)
136 266 , type_resolver(_type_resolver)
137 266 , scope_tracker(_compiler_context)
138 266 , class_type(nullptr)
139 266 , method(nullptr)
140 266 {}
141 266 FunctionDefinitionResolver::~FunctionDefinitionResolver()
142 266 {}
143
144 bool
145 532 FunctionDefinitionResolver::is_method() const
146 {
147 532 return class_type != nullptr;
148 }
149
150 bool
151 266 FunctionDefinitionResolver::resolve()
152 {
153 std::string fully_qualified_function_name =
154 266 function_definition.get_name().get_fully_qualified_name();
155
156 266 fprintf(stderr, " - Extracting function %s\n",
157 fully_qualified_function_name.c_str());
158 266 NS2Entity *entity = function_definition.get_name().get_ns2_entity();
159 266 fprintf(stderr, " - namespace is %s\n", entity->get_parent()->get_fully_qualified_name().c_str());
160
161 266 Type *maybe_class_type = mir.get_types().get_type(entity->get_parent()->get_fully_qualified_name());
162
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 264 times.
266 if (maybe_class_type != nullptr) {
163 2 const auto & method_it = maybe_class_type->get_methods().find(entity->get_name());
164
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if (method_it == maybe_class_type->get_methods().end()) {
165 compiler_context
166 .get_errors()
167 .add_simple_error(
168 function_definition.get_source_ref(),
169 "Member function not declared.",
170 std::string("Member method ")
171 + entity->get_name()
172 + std::string(" was not declared in class ")
173 + entity->get_parent()->get_fully_qualified_name()
174 );
175 return false;
176 }
177 else {
178 // This is a specific member function of a class.
179 // Mark the class and method here.
180 2 class_type = maybe_class_type;
181 2 method = &method_it->second;
182 }
183 }
184
185 // TODO
186 // Here, we should figure out if this is a 'regular' function
187 // or a method of a class. If it's a regular function, fair enough,
188 // but if it's a class, we should make the 'this' argument the first
189 // implicit argument so that the semantics will act like a method call
190 // instead of just a funtion. This impacts the variable resolution also
191 // becuase variables found may be 'local' variables or might be 'this->var'
192 // style variables that automatically get de-referenced from the
193 // implicit object 'this' argument.
194
195 266 const TypeSpecifier & type_specifier = function_definition.get_return_type();
196 266 const Type *return_type = type_resolver.extract_from_type_specifier(type_specifier);
197
198
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 266 times.
266 if (return_type == nullptr) {
199 fprintf(stderr, "Could not find return type\n");
200 return false;
201
202 }
203 266 std::vector<FunctionArgument> arguments;
204
205 // Add the implicit '<this>' argument if this is a method.
206
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 264 times.
266 if (is_method()) {
207 2 const Type *this_type = mir.get_types().get_pointer_to(class_type, function_definition.get_source_ref());
208 2 std::string this_arg_name("<this>");
209 FunctionArgument arg(this_arg_name, this_type,
210 2 function_definition.get_source_ref(),
211 2 function_definition.get_source_ref());
212 2 arguments.push_back(arg);
213 2 }
214
215 266 const auto & function_argument_list = function_definition.get_arguments();
216 266 const auto & function_definition_args = function_argument_list.get_arguments();
217
2/2
✓ Branch 5 taken 518 times.
✓ Branch 6 taken 266 times.
784 for (const auto & function_definition_arg : function_definition_args) {
218 518 std::string name = function_definition_arg->get_identifier().get_name();
219 518 const Gyoji::mir::Type * mir_type = type_resolver.extract_from_type_specifier(function_definition_arg->get_type_specifier());
220
221 FunctionArgument arg(name, mir_type,
222 518 function_definition_arg->get_identifier().get_source_ref(),
223 1036 function_definition_arg->get_type_specifier().get_source_ref());
224 518 arguments.push_back(arg);
225 518 fprintf(stderr, "Function argument %s\n", name.c_str());
226
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 518 times.
518 if (!scope_tracker.add_variable(name, mir_type, function_definition_arg->get_source_ref())) {
227 fprintf(stderr, "Existing, stopping process\n");
228 return false;
229 }
230
2/4
✓ Branch 1 taken 518 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 518 times.
✗ Branch 5 not taken.
518 }
231
232 // Check that the arguments declared here
233 // match the function (or method) declaration,
234 // otherwise we'll end up with a badly defined
235 // function.
236
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 264 times.
266 if (is_method()) {
237 // Argument mismatch from method.
238
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if (arguments.size() != method->get_arguments().size()) {
239 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Method argument mismatch");
240 error->add_message(
241 function_definition.get_source_ref(),
242 std::string("Method has ") + std::to_string(arguments.size()-1) + std::string(" arguments defined")
243 );
244 error->add_message(
245 method->get_source_ref(),
246 std::string("First declared here with ") + std::to_string(method->get_arguments().size()-1)
247 );
248 compiler_context
249 .get_errors()
250 .add_error(std::move(error));
251
252 return false;
253 }
254 2 bool arg_error = false;
255
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
6 for (size_t i = 0; i < arguments.size(); i++) {
256 4 const FunctionArgument & fa = arguments.at(i);
257 4 const Argument & ma = method->get_arguments().at(i);
258
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
4 if (fa.get_type()->get_name() != ma.get_type()->get_name()) {
259 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Method argument mismatch");
260 error->add_message(
261 fa.get_type_source_ref(),
262 std::string("Argument defined as ") + fa.get_type()->get_name() + std::string(" does not match declaration.")
263 );
264 error->add_message(
265 ma.get_source_ref(),
266 std::string("First declared here as ") + ma.get_type()->get_name()
267 );
268 compiler_context
269 .get_errors()
270 .add_error(std::move(error));
271 arg_error = true;
272 }
273 }
274
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (arg_error) {
275 return false;
276 }
277
278 }
279 else {
280 // We should to the same check
281 // against the function declaration for a 'plain' function.
282 264 const Gyoji::mir::Symbol *symbol = mir.get_symbols().get_symbol(fully_qualified_function_name);
283
1/2
✓ Branch 0 taken 264 times.
✗ Branch 1 not taken.
264 if (symbol == nullptr) {
284 // This is perfectly fine. It just
285 // means that there was no forward declaration
286 // for this function.
287 }
288 else {
289 // If there was a forward declaration, we need to make sure
290 // it is the correct type and matches the function signature.
291 264 const Type *symbol_type = symbol->get_type();
292
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 264 times.
264 if (symbol_type->get_type() != Type::TYPE_FUNCTION_POINTER) {
293 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Symbol is not a function");
294 error->add_message(
295 function_definition.get_source_ref(),
296 std::string("Symbol ") + fully_qualified_function_name + std::string(" is not declared as a function.")
297 );
298 compiler_context
299 .get_errors()
300 .add_error(std::move(error));
301 return false;
302 }
303 264 const std::vector<Argument> & function_arguments = symbol_type->get_argument_types();
304
305
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 264 times.
264 if (arguments.size() != function_arguments.size()) {
306 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Method argument mismatch");
307 error->add_message(
308 function_definition.get_source_ref(),
309 std::string("Function has ") + std::to_string(arguments.size()) + std::string(" arguments defined")
310 );
311 error->add_message(
312 symbol_type->get_defined_source_ref(),
313 std::string("First declared here with ") + std::to_string(function_arguments.size())
314 );
315 compiler_context
316 .get_errors()
317 .add_error(std::move(error));
318
319 return false;
320 }
321 264 bool arg_error = false;
322
2/2
✓ Branch 1 taken 516 times.
✓ Branch 2 taken 264 times.
780 for (size_t i = 0; i < arguments.size(); i++) {
323 516 const FunctionArgument & fa = arguments.at(i);
324 516 const Argument & ma = function_arguments.at(i);
325
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 516 times.
516 if (fa.get_type()->get_name() != ma.get_type()->get_name()) {
326 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Method argument mismatch");
327 error->add_message(
328 fa.get_type_source_ref(),
329 std::string("Argument defined as ") + fa.get_type()->get_name() + std::string(" does not match declaration.")
330 );
331 error->add_message(
332 ma.get_source_ref(),
333 std::string("First declared here as ") + ma.get_type()->get_name()
334 );
335 compiler_context
336 .get_errors()
337 .add_error(std::move(error));
338 arg_error = true;
339 }
340 }
341
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 264 times.
264 if (arg_error) {
342 return false;
343 }
344 }
345
346
347 }
348
349 532 function = std::make_unique<Function>(
350 fully_qualified_function_name,
351 return_type,
352 arguments,
353 532 function_definition.get_source_ref());
354
355
356 // Create a new basic block for the start
357
358 266 current_block = function->add_block();
359
360
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 266 times.
266 if (!extract_from_statement_list(
361 266 function_definition.get_scope_body().get_statements())) {
362 return false;
363 }
364
365 //scope_tracker.dump();
366
367 // This is a list of goto operations (ScopeOperation*)
368 // each with a vector of variable declarations (std::vector<ScopeOperation*>)
369 // that represent variables that should be 'undeclared' just prior to
370 // the Jump instruction of each basic block. The first element
371 // of the pair will always be a Goto and the second will always be
372 // variable declarations. The point of this is to collect all of them
373 // here because they aren't known earlier while the funciton is still under
374 // construction so that we can insert the required instructions
375 // now that we know what should be going out of scope
376 // for the goto statements.
377 266 std::vector<std::pair<const ScopeOperation*, std::vector<const ScopeOperation*>>> goto_fixups;
378
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 266 times.
266 if (!scope_tracker.check(goto_fixups)) {
379 return false;
380 }
381
382
2/2
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 266 times.
268 for (const auto & fixup : goto_fixups) {
383 2 const ScopeOperation *goto_operation = fixup.first;
384 2 size_t basic_block_id = goto_operation->get_goto_point().get_basic_block_id();
385 2 size_t location = goto_operation->get_goto_point().get_location();
386 2 fprintf(stderr, "Processing fixup for goto %s %ld %ld\n",
387 2 goto_operation->get_goto_label().c_str(),
388 basic_block_id,
389 location
390 );
391
2/2
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 2 times.
6 for (const auto & unwind : fixup.second) {
392 4 fprintf(stderr, "Unwinding variable %s\n", unwind->get_variable_name().c_str());
393
394 // TODO:
395 // when we implement destructors, they will be called here
396 // just before we undeclare the variables.
397
398 // Insert the 'undeclare' operation to mark that
399 // this variable is no longer in scope.
400 auto unwind_operation = std::make_unique<OperationLocalUndeclare>(
401 goto_operation->get_source_ref(),
402 4 unwind->get_variable_name());
403 4 function->get_basic_block(basic_block_id)
404 4 .insert_operation(location, std::move(unwind_operation));
405
406 // This increment is what makes sure that
407 // the unwind operations are emitted in the
408 // correct order so that variable destructors
409 // are called in the reverse order as the
410 // declarations.
411 4 location++;
412 4 }
413 }
414
415 266 mir.get_functions().add_function(std::move(function));
416
417 266 return true;
418 266 }
419
420 bool
421 1242 FunctionDefinitionResolver::extract_from_expression_primary_identifier(
422 size_t & returned_tmpvar,
423 const Gyoji::frontend::tree::ExpressionPrimaryIdentifier & expression
424 )
425 {
426 // At this point, we should try to identify what this
427 // identifier actually refers to.
428 // * First look inside the function's declared variables in scope.
429 // * Next, look in the global namespace for functions.
430 // * Finally, look in the global namespace for external
431 // variables like 'global' or 'static' variables.
432 // If the identifier is a function.
433 // There are a few cases:
434 // * The it is a function and is used in an assignment
435 // to a function pointer. In this case, we should
436 // return the type as a function pointer type
437 // * If it is used in a function call directly, we should not
438 // emit the code, but should just use an 'immediate'
439 // execution of whatever it is.
440 // * Otherwise, emit it as a 'load' and just follow our nose
441 // at runtime.
442 // * Maybe we really should 'flatten' our access here.
443
444
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1242 times.
1242 if (expression.get_identifier().get_identifier_type() == Terminal::IDENTIFIER_LOCAL_SCOPE) {
445 // This block is obsolete, it should
446 // no longer be possible to get here, so we should
447 // confirm that fact and remove this block altogether.
448 compiler_context
449 .get_errors()
450 .add_simple_error(
451 expression.get_source_ref(),
452 "Local variable could not be resolved: should not be reachable.",
453 std::string("Local variable ") + expression.get_identifier().get_value() + std::string(" was not found in this scope.")
454 );
455 return false;
456 }
457
1/2
✓ Branch 2 taken 1242 times.
✗ Branch 3 not taken.
1242 else if (expression.get_identifier().get_identifier_type() == Terminal::IDENTIFIER_GLOBAL_SCOPE) {
458 // First, check to see if there is a variable of that name
459 // declared in our current scope.
460 1242 const LocalVariable *localvar = scope_tracker.get_variable(
461 2484 expression.get_identifier().get_name()
462 );
463
2/2
✓ Branch 0 taken 1176 times.
✓ Branch 1 taken 66 times.
1242 if (localvar != nullptr) {
464 1176 returned_tmpvar = function->tmpvar_define(localvar->get_type());
465 auto operation = std::make_unique<OperationLocalVariable>(
466 1176 expression.get_identifier().get_source_ref(),
467 returned_tmpvar,
468 2352 expression.get_identifier().get_name(),
469 1176 localvar->get_type()
470 1176 );
471 1176 function->get_basic_block(current_block).add_operation(std::move(operation));
472
473 1176 return true;
474 1176 }
475
476 // Next, check for member variable and insert the 'dereference' operation
477 // and then a 'dot' operation to get the actual value of the variable.
478 // temp var for the operation should be an argument that we've defined in the
479 // scope, but only addressable by 'member' name (i.e. there is no 'this' argument
480 // according to the method).
481
482
483 // Look in the list of functions,
484 // this might be a function pointer assignment or
485 // a global variable.
486 132 const Gyoji::mir::Symbol *symbol = mir.get_symbols().get_symbol(
487 132 expression.get_identifier().get_fully_qualified_name()
488 );
489
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
66 if (symbol == nullptr) {
490 compiler_context
491 .get_errors()
492 .add_simple_error(
493 expression.get_source_ref(),
494 "Unresolved symbol",
495 std::string("Local variable ") + expression.get_identifier().get_fully_qualified_name() + std::string(" was not found in this scope.")
496 );
497 return false;
498 }
499 66 returned_tmpvar = function->tmpvar_define(symbol->get_type());
500
501 auto operation = std::make_unique<OperationSymbol>(
502 66 expression.get_identifier().get_source_ref(),
503 returned_tmpvar,
504 132 expression.get_identifier().get_fully_qualified_name()
505 66 );
506 66 function->get_basic_block(current_block).add_operation(std::move(operation));
507 66 return true;
508 66 }
509 return false;
510 }
511
512 bool
513 4 FunctionDefinitionResolver::extract_from_expression_primary_nested(
514 size_t & returned_tmpvar,
515 const Gyoji::frontend::tree::ExpressionPrimaryNested & expression)
516 {
517 // Nested expressions don't emit blocks on their own, just run whatever is nested.
518 4 return extract_from_expression(
519 returned_tmpvar,
520 expression.get_expression()
521 4 );
522 }
523
524 bool
525 8 FunctionDefinitionResolver::extract_from_expression_primary_literal_char(
526 size_t & returned_tmpvar,
527 const Gyoji::frontend::tree::ExpressionPrimaryLiteralChar & expression)
528 {
529 8 std::string string_unescaped;
530 size_t location;
531 8 bool escape_success = Gyoji::misc::string_c_unescape(string_unescaped, location, expression.get_value(), true);
532 char c;
533
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (!escape_success) {
534 compiler_context
535 .get_errors()
536 .add_simple_error(
537 expression.get_source_ref(),
538 "Invalid Character Literal",
539 std::string("Unknown escape sequence found at character offset ") + std::to_string(location) + std::string(" in character literal")
540 );
541 c = '!';
542 }
543 else {
544
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (string_unescaped.size() != 1) {
545 compiler_context
546 .get_errors()
547 .add_simple_error(
548 expression.get_source_ref(),
549 "Invalid Character Literal",
550 std::string("Character literal must consist of a single byte.")
551 );
552 c = '%';
553 }
554 else {
555 // Finally, we're sure it's valid and
556 // has one element, we can pass it down
557 // to the operation.
558 8 c = string_unescaped[0];
559 }
560 }
561
562
563 16 returned_tmpvar = function->tmpvar_define(mir.get_types().get_type("u8"));
564 auto operation = std::make_unique<OperationLiteralChar>(
565 expression.get_source_ref(),
566 returned_tmpvar,
567 c
568 8 );
569
570 function
571 8 ->get_basic_block(current_block)
572 8 .add_operation(std::move(operation));
573 16 return true;
574 8 }
575
576 bool
577 10 FunctionDefinitionResolver::extract_from_expression_primary_literal_string(
578 size_t & returned_tmpvar,
579 const Gyoji::frontend::tree::ExpressionPrimaryLiteralString & expression)
580 {
581 // The hardest part here is that we need to extract the escape sequences from
582 // the source representation and place the raw data into the operation
583 // so that it is working with the actual literal value that will be
584 // placed into the machine code during the code-generation stage.
585
586 10 std::string string_unescaped;
587 size_t location;
588 10 bool escape_success = Gyoji::misc::string_c_unescape(string_unescaped, location, expression.get_value(), false);
589
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (!escape_success) {
590 compiler_context
591 .get_errors()
592 .add_simple_error(
593 expression.get_source_ref(),
594 "Invalid String Literal",
595 std::string("Unknown escape sequence found at character offset ") + std::to_string(location) + std::string(" in string")
596 );
597 // We can actually continue processing
598 // the file so we can extract more errors later.
599 // We've already found an error, so there's no danger
600 // or producing an invalid file because the code-generator
601 // won't run if there's an error.
602 }
603
604
605
606 20 returned_tmpvar = function->tmpvar_define(mir.get_types().get_type("u8*"));
607 auto operation = std::make_unique<OperationLiteralString>(
608 expression.get_source_ref(),
609 returned_tmpvar,
610 string_unescaped
611 10 );
612 function
613 10 ->get_basic_block(current_block)
614 10 .add_operation(std::move(operation));
615 20 return true;
616 10 }
617
618 bool
619 2 FunctionDefinitionResolver::create_constant_integer_one(
620 const Gyoji::mir::Type *type,
621 size_t & returned_tmpvar,
622 const Gyoji::context::SourceReference & _src_ref
623 )
624 {
625 Gyoji::frontend::integers::ParseLiteralIntResult parse_result;
626 2 parse_result.parsed_type = type;
627
628
1/9
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2 switch (type->get_type()) {
629 case Type::TYPE_PRIMITIVE_u8:
630 parse_result.u8_value = (unsigned char)1;
631 break;
632 case Type::TYPE_PRIMITIVE_u16:
633 parse_result.u16_value = (unsigned short)1;
634 break;
635 2 case Type::TYPE_PRIMITIVE_u32:
636 2 parse_result.u32_value = (unsigned int)1;
637 2 break;
638 case Type::TYPE_PRIMITIVE_u64:
639 parse_result.u64_value = (unsigned long)1;
640 break;
641 case Type::TYPE_PRIMITIVE_i8:
642 parse_result.i8_value = (char)1;
643 break;
644 case Type::TYPE_PRIMITIVE_i16:
645 parse_result.i16_value = (short)1;
646 break;
647 case Type::TYPE_PRIMITIVE_i32:
648 parse_result.i32_value = (int)1;
649 break;
650 case Type::TYPE_PRIMITIVE_i64:
651 parse_result.i64_value = (long)1;
652 break;
653 default:
654 compiler_context
655 .get_errors()
656 .add_simple_error(
657 _src_ref,
658 "Compiler Bug! Invalid integer literal",
659 std::string("Unsupported primitive literal type creating literal one value") + type->get_name()
660 );
661 return false;
662 }
663 2 return create_constant_integer(parse_result, returned_tmpvar, _src_ref);
664 }
665
666 bool
667 222 FunctionDefinitionResolver::create_constant_integer(
668 const Gyoji::frontend::integers::ParseLiteralIntResult & parse_result,
669 size_t & returned_tmpvar,
670 const Gyoji::context::SourceReference & _src_ref
671 )
672 {
673 222 const Type *type_part = parse_result.parsed_type;
674 222 returned_tmpvar = function->tmpvar_define(type_part);
675
676 222 Gyoji::owned<Gyoji::mir::Operation> operation;
677
8/9
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 146 times.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 8 times.
✓ Branch 8 taken 10 times.
✗ Branch 9 not taken.
222 switch (type_part->get_type()) {
678 10 case Type::TYPE_PRIMITIVE_u8:
679 {
680 10 operation = std::make_unique<OperationLiteralInt>(
681 _src_ref,
682 returned_tmpvar,
683 20 type_part->get_type(),
684 10 parse_result.u8_value
685 10 );
686 }
687 24 case Type::TYPE_PRIMITIVE_u16:
688 {
689 24 operation = std::make_unique<OperationLiteralInt>(
690 _src_ref,
691 returned_tmpvar,
692 24 type_part->get_type(),
693 24 parse_result.u16_value
694 24 );
695 }
696 24 break;
697 146 case Type::TYPE_PRIMITIVE_u32:
698 {
699 146 operation = std::make_unique<OperationLiteralInt>(
700 _src_ref,
701 returned_tmpvar,
702 146 type_part->get_type(),
703 146 parse_result.u32_value
704 146 );
705 }
706 146 break;
707 18 case Type::TYPE_PRIMITIVE_u64:
708 {
709 18 operation = std::make_unique<OperationLiteralInt>(
710 _src_ref,
711 returned_tmpvar,
712 18 type_part->get_type(),
713 18 parse_result.u64_value
714 18 );
715 }
716 18 break;
717
718 // Signed
719 10 case Type::TYPE_PRIMITIVE_i8:
720 {
721 10 operation = std::make_unique<OperationLiteralInt>(
722 _src_ref,
723 returned_tmpvar,
724 20 type_part->get_type(),
725 10 parse_result.i8_value
726 10 );
727 }
728 16 case Type::TYPE_PRIMITIVE_i16:
729 {
730 16 operation = std::make_unique<OperationLiteralInt>(
731 _src_ref,
732 returned_tmpvar,
733 16 type_part->get_type(),
734 16 parse_result.i16_value
735 16 );
736 }
737 16 break;
738 8 case Type::TYPE_PRIMITIVE_i32:
739 {
740 8 operation = std::make_unique<OperationLiteralInt>(
741 _src_ref,
742 returned_tmpvar,
743 8 type_part->get_type(),
744 8 parse_result.i32_value
745 8 );
746 }
747 8 break;
748 10 case Type::TYPE_PRIMITIVE_i64:
749 {
750 10 operation = std::make_unique<OperationLiteralInt>(
751 _src_ref,
752 returned_tmpvar,
753 10 type_part->get_type(),
754 10 parse_result.i64_value
755 10 );
756 }
757 10 break;
758
759 default:
760 compiler_context
761 .get_errors()
762 .add_simple_error(
763 _src_ref,
764 "Compiler Bug! Invalid integer literal",
765 std::string("Unsupported primitive literal type ") + type_part->get_name()
766 );
767 return false;
768 }
769 function
770 222 ->get_basic_block(current_block)
771 222 .add_operation(std::move(operation));
772
773 222 return true;
774 222 }
775
776 // There is a LOT of logic here for handling various
777 // kinds of integer literals that might be specified here.
778 // Of course, the simplest is something like
779 // u32 a = 10;
780 // We also support more interesting
781 // things like hexidecimal, octal, and binary
782 // such at:
783 // u64 a = 0xfeeda747u64;
784 // u16 b = 0o03423;
785 // We also allow separators for readability, consider:
786 // u64 h = 0xfeed_a_747_u64;
787 // u16 o = 0o034_23;
788 // u16 b = 0b0100_0200_u16;
789 // Combine that with range-checking all of the
790 // possible representations, and you have a lot of things
791 // that can go wrong such as a number being badly formatted
792 // or with characters outside the radix.
793 // Condier what happens when the user specifies these:
794 // u8 n = 342343u8; // Number too big for u8
795 // u8 n = -12u8; // It's an unsigned, but you put a negative there.
796 // u8 n = 0b23334u8; // It's a binary number, but it's not really binary (and too big also).
797 // SO many error messages so that the user knows PRECISELY what they did wrong and where.
798 bool
799 220 FunctionDefinitionResolver::extract_from_expression_primary_literal_int(
800 size_t & returned_tmpvar,
801 const Gyoji::frontend::tree::ExpressionPrimaryLiteralInt & expression)
802 {
803 Gyoji::frontend::integers::ParseLiteralIntResult parse_result;
804 220 const Terminal & literal_int_token = expression.get_literal_int_token();
805 220 bool parsed = parse_literal_int(compiler_context, mir.get_types(), literal_int_token, parse_result);
806
2/4
✓ Branch 0 taken 220 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 220 times.
220 if (!parsed || parse_result.parsed_type == nullptr) {
807 return false;
808 }
809
810 220 return create_constant_integer(
811 parse_result,
812 returned_tmpvar,
813 220 literal_int_token.get_source_ref());
814 }
815
816 bool
817 6 FunctionDefinitionResolver::extract_from_expression_primary_literal_float(
818 size_t & returned_tmpvar,
819 const Gyoji::frontend::tree::ExpressionPrimaryLiteralFloat & expression)
820 {
821 6 std::string literal_type_name = expression.get_type();
822 6 returned_tmpvar = function->tmpvar_define(mir.get_types().get_type(literal_type_name));
823 6 Gyoji::owned<OperationLiteralFloat> operation;
824 char *endptr;
825 6 const char *source_cstring = expression.get_value().c_str();
826 6 size_t length = expression.get_value().size();
827 6 errno = 0;
828
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
6 if (literal_type_name == "f32") {
829
830 2 float converted_value = strtof(source_cstring, &endptr);
831
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (endptr != (source_cstring + length)) {
832 compiler_context
833 .get_errors()
834 .add_simple_error(
835 expression.get_source_ref(),
836 "Invalid floating-point literal",
837 std::string("Could not correctly parse the literal value.")
838 );
839 return false;
840 }
841
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (errno == ERANGE) {
842 compiler_context
843 .get_errors()
844 .add_simple_error(
845 expression.get_source_ref(),
846 "Invalid floating-point literal",
847 std::string("Floating-point literal does not fit in the range of an f32.")
848 );
849 return false;
850 }
851 4 operation = std::make_unique<OperationLiteralFloat>(
852 expression.get_source_ref(),
853 returned_tmpvar,
854 4 (float)converted_value
855 2 );
856 }
857 else {
858 4 double converted_value = strtod(source_cstring, &endptr);
859
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (endptr != (source_cstring + length)) {
860 compiler_context
861 .get_errors()
862 .add_simple_error(
863 expression.get_source_ref(),
864 "Invalid floating-point literal",
865 std::string("Could not correctly parse the literal value.")
866 );
867 return false;
868 }
869
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (errno == ERANGE) {
870 compiler_context
871 .get_errors()
872 .add_simple_error(
873 expression.get_source_ref(),
874 "Invalid floating-point literal",
875 std::string("Floating-point literal does not fit in the range of an f64.")
876 );
877 return false;
878 }
879 8 operation = std::make_unique<OperationLiteralFloat>(
880 expression.get_source_ref(),
881 returned_tmpvar,
882 converted_value
883 4 );
884 }
885 function
886 6 ->get_basic_block(current_block)
887 6 .add_operation(std::move(operation));
888 6 return true;
889 6 }
890
891 bool
892 10 FunctionDefinitionResolver::extract_from_expression_postfix_array_index(
893 size_t & returned_tmpvar,
894 const ExpressionPostfixArrayIndex & expression)
895 {
896 size_t array_tmpvar;
897 size_t index_tmpvar;
898
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
10 if (!extract_from_expression(array_tmpvar, expression.get_array())) {
899 return false;
900 }
901
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
10 if (!extract_from_expression(index_tmpvar, expression.get_index())) {
902 return false;
903 }
904
905 10 const Type *array_type = function->tmpvar_get(array_tmpvar);
906
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 if (!array_type->is_array()) {
907 compiler_context
908 .get_errors()
909 .add_simple_error(
910 expression.get_array().get_source_ref(),
911 "Array type must be an array type",
912 std::string("Type of array is not an array type.")
913 );
914 return false;
915 }
916
917 10 const Type *index_type = function->tmpvar_get(index_tmpvar);
918
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 if (index_type->get_type() != Type::TYPE_PRIMITIVE_u32) {
919 compiler_context
920 .get_errors()
921 .add_simple_error(
922 expression.get_index().get_source_ref(),
923 "Array index must be an unsigned 32-bit (u32) type",
924 std::string("Type of index is not a u32 index")
925 );
926 return false;
927 }
928
929 10 returned_tmpvar = function->tmpvar_define(array_type->get_pointer_target());
930 auto operation = std::make_unique<OperationArrayIndex>(
931 expression.get_source_ref(),
932 returned_tmpvar,
933 array_tmpvar,
934 index_tmpvar
935 10 );
936 function
937 10 ->get_basic_block(current_block)
938 10 .add_operation(std::move(operation));
939
940 10 return true;
941 10 }
942
943 bool
944 66 FunctionDefinitionResolver::extract_from_expression_postfix_function_call(
945 size_t & returned_tmpvar,
946 const ExpressionPostfixFunctionCall & expression)
947 {
948 // Extract the expression itself from the arguments.
949 size_t function_type_tmpvar;
950
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 66 times.
66 if (!extract_from_expression(function_type_tmpvar, expression.get_function())) {
951 fprintf(stderr, "Not extracting function early return\n");
952 return false;
953 }
954
955 66 std::vector<size_t> arg_types;
956
2/2
✓ Branch 7 taken 52 times.
✓ Branch 8 taken 66 times.
118 for (const auto & arg_expr : expression.get_arguments().get_arguments()) {
957 size_t arg_returned_value;
958
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 52 times.
52 if (!extract_from_expression(arg_returned_value, *arg_expr)) {
959 fprintf(stderr, "Not extracting function arg expression\n");
960 return false;
961 }
962 52 arg_types.push_back(arg_returned_value);
963 }
964
965 66 const Type *function_pointer_type = function->tmpvar_get(function_type_tmpvar);
966
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 66 times.
66 if (function_pointer_type->get_type() != Type::TYPE_FUNCTION_POINTER) {
967 fprintf(stderr, "Not extracting function invalid type\n");
968 compiler_context
969 .get_errors()
970 .add_simple_error(
971 expression.get_function().get_source_ref(),
972 "Called object is not a function.",
973 std::string("Type of object being called is not a function, but is a ") + function_pointer_type->get_name() + std::string(" instead.")
974 );
975 return false;
976 }
977
978 // We declare that we return the vale that the function
979 // will return.
980 66 returned_tmpvar = function->tmpvar_define(function_pointer_type->get_return_type());
981
982 auto operation = std::make_unique<OperationFunctionCall>(
983 expression.get_source_ref(),
984 returned_tmpvar,
985 function_type_tmpvar,
986 arg_types
987 66 );
988
989 function
990 66 ->get_basic_block(current_block)
991 66 .add_operation(std::move(operation));
992
993 66 return true;
994 66 }
995
996 bool
997 10 FunctionDefinitionResolver::extract_from_expression_postfix_dot(
998 size_t & returned_tmpvar,
999 const ExpressionPostfixDot & expression)
1000 {
1001 size_t class_tmpvar;
1002
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
10 if (!extract_from_expression(class_tmpvar, expression.get_expression())) {
1003 return false;
1004 }
1005
1006 10 const Type *class_type = function->tmpvar_get(class_tmpvar);
1007
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 if (class_type->get_type() != Type::TYPE_COMPOSITE) {
1008 compiler_context
1009 .get_errors()
1010 .add_simple_error(
1011 expression.get_expression().get_source_ref(),
1012 "Member access must be applied to a class.",
1013 std::string("Type of object being accessed is not a class, but is a ") + class_type->get_name() + std::string(" instead.")
1014 );
1015 return false;
1016 }
1017 10 const std::string & member_name = expression.get_identifier().get_name();
1018
1019 10 const TypeMember *member = class_type->member_get(member_name);
1020
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (member == nullptr) {
1021 compiler_context
1022 .get_errors()
1023 .add_simple_error(
1024 expression.get_expression().get_source_ref(),
1025 "Class does not have this member.",
1026 std::string("Class does not have member '") + member_name + std::string("'.")
1027 );
1028 }
1029
1030 10 returned_tmpvar = function->tmpvar_define(member->get_type());
1031 auto operation = std::make_unique<OperationDot>(
1032 expression.get_source_ref(),
1033 returned_tmpvar,
1034 class_tmpvar,
1035 member_name
1036 10 );
1037 function
1038 10 ->get_basic_block(current_block)
1039 10 .add_operation(std::move(operation));
1040 10 return true;
1041 10 }
1042
1043 bool
1044 2 FunctionDefinitionResolver::extract_from_expression_postfix_arrow(
1045 size_t & returned_tmpvar,
1046 const ExpressionPostfixArrow & expression)
1047 {
1048 size_t classptr_tmpvar;
1049
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_expression(classptr_tmpvar, expression.get_expression())) {
1050 return false;
1051 }
1052
1053 2 const Type *classptr_type = function->tmpvar_get(classptr_tmpvar);
1054
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (classptr_type->get_type() != Type::TYPE_POINTER) {
1055 compiler_context
1056 .get_errors()
1057 .add_simple_error(
1058 expression.get_expression().get_source_ref(),
1059 "Arrow (->) operator must be used on a pointer to a class.",
1060 std::string("Type of object being accessed is not a pointer to a class, but is a ") + classptr_type->get_name() + std::string(" instead.")
1061 );
1062 return false;
1063 }
1064 2 const Type *class_type = classptr_type->get_pointer_target();
1065
1066
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (class_type->get_type() != Type::TYPE_COMPOSITE) {
1067 compiler_context
1068 .get_errors()
1069 .add_simple_error(
1070 expression.get_expression().get_source_ref(),
1071 "Arrow (->) access must be applied to a pointer to a class.",
1072 std::string("Type of object being accessed is not a pointer to a class , but is a pointer to ") + class_type->get_name() + std::string(" instead.")
1073 );
1074 return false;
1075 }
1076
1077 // First takes 'operand' as a pointer
1078 // and de-references it into an lvalue.
1079 2 size_t class_reference_tmpvar = function->tmpvar_define(class_type);
1080 auto dereference_operation = std::make_unique<OperationUnary>(
1081 4 Operation::OP_DEREFERENCE,
1082 expression.get_source_ref(),
1083 class_reference_tmpvar,
1084 classptr_tmpvar
1085 2 );
1086
1087 function
1088 2 ->get_basic_block(current_block)
1089 2 .add_operation(std::move(dereference_operation));
1090
1091 2 const std::string & member_name = expression.get_identifier().get_name();
1092 2 const TypeMember *member = class_type->member_get(member_name);
1093
1094 2 returned_tmpvar = function->tmpvar_define(member->get_type());
1095 auto dot_operation = std::make_unique<OperationDot>(
1096 expression.get_source_ref(),
1097 returned_tmpvar,
1098 class_reference_tmpvar,
1099 member_name
1100 2 );
1101 function
1102 2 ->get_basic_block(current_block)
1103 2 .add_operation(std::move(dot_operation));
1104 2 return true;
1105
1106 2 }
1107
1108 bool
1109 2 FunctionDefinitionResolver::extract_from_expression_postfix_incdec(
1110 size_t & returned_tmpvar,
1111 const ExpressionPostfixIncDec & expression)
1112 {
1113 size_t operand_tmpvar;
1114
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_expression(operand_tmpvar, expression.get_expression())) {
1115 return false;
1116 }
1117
1118 2 return create_incdec_operation(
1119 expression.get_source_ref(),
1120 returned_tmpvar,
1121 operand_tmpvar,
1122 2 (expression.get_type() == ExpressionPostfixIncDec::INCREMENT), // is_increment
1123 true // is_postfix
1124 2 );
1125 }
1126
1127 bool
1128 2 FunctionDefinitionResolver::create_incdec_operation(
1129 const Gyoji::context::SourceReference & src_ref,
1130 size_t & returned_tmpvar,
1131 const size_t & operand_tmpvar,
1132 bool is_increment,
1133 bool is_postfix
1134 )
1135 {
1136 // This should be implemented as:
1137 // _1 = load(variable)
1138 // _2 = constant(1)
1139 // _3 = add/sub _1 _2 <==== Add or subtract depending on is_increment
1140 // store(_3, variable)
1141 // _4 = <==== This will be either _1 or _3 depending on is_postfix.
1142 //
1143
1144 2 const Type *operand_type = function->tmpvar_get(operand_tmpvar);
1145 size_t constant_one_tmpvar;
1146
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!create_constant_integer_one(
1147 operand_type,
1148 constant_one_tmpvar,
1149 src_ref
1150 )) {
1151 return false;
1152 }
1153
1154
1155 2 size_t addresult_tmpvar = function->tmpvar_duplicate(operand_tmpvar);
1156
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (is_increment) {
1157 auto operation = std::make_unique<OperationBinary>(
1158 4 Operation::OP_ADD,
1159 src_ref,
1160 addresult_tmpvar,
1161 operand_tmpvar,
1162 constant_one_tmpvar
1163 2 );
1164 function
1165 2 ->get_basic_block(current_block)
1166 2 .add_operation(std::move(operation));
1167 2 }
1168 else {
1169 auto operation = std::make_unique<OperationBinary>(
1170 Operation::OP_SUBTRACT,
1171 src_ref,
1172 addresult_tmpvar,
1173 operand_tmpvar,
1174 constant_one_tmpvar
1175 );
1176 function
1177 ->get_basic_block(current_block)
1178 .add_operation(std::move(operation));
1179 }
1180
1181 // We perform a 'store' to store
1182 // the value back into the variable.
1183 2 size_t ignore_tmpvar = function->tmpvar_duplicate(operand_tmpvar);
1184 auto operation_store = std::make_unique<OperationBinary>(
1185 4 Operation::OP_ASSIGN,
1186 src_ref,
1187 ignore_tmpvar,
1188 operand_tmpvar,
1189 addresult_tmpvar
1190 2 );
1191 function
1192 2 ->get_basic_block(current_block)
1193 2 .add_operation(std::move(operation_store));
1194
1195 // This is a post-decrement, so we return
1196 // the value as it was before we incremented
1197 // it.
1198
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 returned_tmpvar = is_postfix ? operand_tmpvar : addresult_tmpvar;
1199 2 return true;
1200 2 }
1201
1202 bool
1203 20 FunctionDefinitionResolver::extract_from_expression_unary_prefix(
1204 size_t & returned_tmpvar,
1205 const ExpressionUnaryPrefix & expression)
1206 {
1207
1208 // Extract the prior expression
1209 // and if not otherwise specified,
1210 // the operation will return the
1211 // same type as the operand.
1212 size_t operand_tmpvar;
1213
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 20 times.
20 if (!extract_from_expression(
1214 operand_tmpvar,
1215 expression.get_expression()
1216 )) {
1217 return false;
1218 }
1219
1220 20 const Type *operand_type = function->tmpvar_get(operand_tmpvar);
1221
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (operand_type == nullptr) {
1222 compiler_context
1223 .get_errors()
1224 .add_simple_error(
1225 expression.get_source_ref(),
1226 "Compiler bug! Please report this message(4)",
1227 "Operand must be a valid type."
1228 );
1229 }
1230
1231 20 ExpressionUnaryPrefix::OperationType op_type = expression.get_type();
1232
3/9
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
20 switch (op_type) {
1233 case ExpressionUnaryPrefix::INCREMENT:
1234 {
1235 return create_incdec_operation(
1236 expression.get_source_ref(),
1237 returned_tmpvar,
1238 operand_tmpvar,
1239 true, // is_increment
1240 false // is_postfix
1241 );
1242 }
1243 break;
1244 case ExpressionUnaryPrefix::DECREMENT:
1245 {
1246 return create_incdec_operation(
1247 expression.get_source_ref(),
1248 returned_tmpvar,
1249 operand_tmpvar,
1250 false, // is_increment
1251 false // is_postfix
1252 );
1253 }
1254 break;
1255 8 case ExpressionUnaryPrefix::ADDRESSOF:
1256 {
1257 8 const Type * pointer_to_operand_type = mir.get_types().get_pointer_to(operand_type, expression.get_source_ref());
1258 8 returned_tmpvar = function->tmpvar_define(pointer_to_operand_type);
1259 auto operation = std::make_unique<OperationUnary>(
1260 16 Operation::OP_ADDRESSOF,
1261 expression.get_source_ref(),
1262 returned_tmpvar,
1263 operand_tmpvar
1264 8 );
1265 function
1266 8 ->get_basic_block(current_block)
1267 8 .add_operation(std::move(operation));
1268 8 }
1269 8 break;
1270 10 case ExpressionUnaryPrefix::DEREFERENCE:
1271 {
1272
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 if (operand_type->get_type() != Type::TYPE_POINTER) {
1273 compiler_context
1274 .get_errors()
1275 .add_simple_error(
1276 expression.get_expression().get_source_ref(),
1277 "Cannot dereference non-pointer",
1278 std::string("Attempting to de-reference non-pointer type ") + operand_type->get_name()
1279 );
1280 return false;
1281 }
1282 10 returned_tmpvar = function->tmpvar_define(operand_type->get_pointer_target());
1283 auto operation = std::make_unique<OperationUnary>(
1284 20 Operation::OP_DEREFERENCE,
1285 expression.get_source_ref(),
1286 returned_tmpvar,
1287 operand_tmpvar
1288 10 );
1289 function
1290 10 ->get_basic_block(current_block)
1291 10 .add_operation(std::move(operation));
1292 10 }
1293 10 break;
1294 case ExpressionUnaryPrefix::PLUS:
1295 {
1296 // Unary plus does nothing, really, so why bother? We just don't
1297 // bother to do anything and just wire the return into the operand
1298 // directly instead of creating a new tmpvar and assigning it.
1299 returned_tmpvar = operand_tmpvar;
1300 }
1301 break;
1302 case ExpressionUnaryPrefix::MINUS:
1303 {
1304 returned_tmpvar = function->tmpvar_duplicate(operand_tmpvar);
1305 auto operation = std::make_unique<OperationUnary>(
1306 Operation::OP_NEGATE,
1307 expression.get_source_ref(),
1308 returned_tmpvar,
1309 operand_tmpvar
1310 );
1311 function
1312 ->get_basic_block(current_block)
1313 .add_operation(std::move(operation));
1314 }
1315 break;
1316 case ExpressionUnaryPrefix::BITWISE_NOT:
1317 {
1318 returned_tmpvar = function->tmpvar_duplicate(operand_tmpvar);
1319 auto operation = std::make_unique<OperationUnary>(
1320 Operation::OP_BITWISE_NOT,
1321 expression.get_source_ref(),
1322 returned_tmpvar,
1323 operand_tmpvar
1324 );
1325 function
1326 ->get_basic_block(current_block)
1327 .add_operation(std::move(operation));
1328 }
1329 break;
1330 2 case ExpressionUnaryPrefix::LOGICAL_NOT:
1331 {
1332
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if (!function->tmpvar_get(operand_tmpvar)->is_bool()) {
1333 compiler_context
1334 .get_errors()
1335 .add_simple_error(
1336 expression.get_expression().get_source_ref(),
1337 "Logical not (!) must operate on 'bool' expressions.",
1338 std::string("Type of condition expression should be 'bool' and was ") + function->tmpvar_get(operand_tmpvar)->get_name()
1339 );
1340 }
1341 2 returned_tmpvar = function->tmpvar_duplicate(operand_tmpvar);
1342 auto operation = std::make_unique<OperationUnary>(
1343 4 Operation::OP_LOGICAL_NOT,
1344 expression.get_source_ref(),
1345 returned_tmpvar,
1346 operand_tmpvar
1347 2 );
1348 function
1349 2 ->get_basic_block(current_block)
1350 2 .add_operation(std::move(operation));
1351 2 }
1352 2 break;
1353 default:
1354 compiler_context
1355 .get_errors()
1356 .add_simple_error(
1357 expression.get_expression().get_source_ref(),
1358 "Compiler Bug!",
1359 "Encountered unknown unary prefix expression"
1360 );
1361 return false;
1362 }
1363 20 return true;
1364 }
1365 bool
1366 8 FunctionDefinitionResolver::extract_from_expression_unary_sizeof_type(
1367 size_t & returned_tmpvar,
1368 const ExpressionUnarySizeofType & expression)
1369 {
1370 8 const Type * operand_type = type_resolver.extract_from_type_specifier(expression.get_type_specifier());
1371 16 const Type * u64_type = mir.get_types().get_type("u64");
1372 8 returned_tmpvar = function->tmpvar_define(u64_type);
1373 auto operation = std::make_unique<OperationSizeofType>(
1374 expression.get_source_ref(),
1375 returned_tmpvar,
1376 operand_type
1377 8 );
1378 function
1379 8 ->get_basic_block(current_block)
1380 8 .add_operation(std::move(operation));
1381 16 return true;
1382 8 }
1383
1384 bool
1385 136 FunctionDefinitionResolver::numeric_widen(
1386 const Gyoji::context::SourceReference & _src_ref,
1387 size_t & _widen_var,
1388 const Type *widen_to
1389 )
1390 {
1391 // The new b is the result of widening b
1392 // to the type of a.
1393 Operation::OperationType widen_type;
1394
6/6
✓ Branch 1 taken 120 times.
✓ Branch 2 taken 16 times.
✓ Branch 4 taken 60 times.
✓ Branch 5 taken 60 times.
✓ Branch 6 taken 60 times.
✓ Branch 7 taken 76 times.
136 if (widen_to->is_integer() && widen_to->is_signed()) {
1395 60 widen_type = Operation::OP_WIDEN_SIGNED;
1396 }
1397
5/6
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 16 times.
✓ Branch 4 taken 60 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 60 times.
✓ Branch 7 taken 16 times.
76 else if (widen_to->is_integer() && widen_to->is_unsigned()) {
1398 60 widen_type = Operation::OP_WIDEN_UNSIGNED;
1399 }
1400
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 else if (widen_to->is_float()) {
1401 16 widen_type = Operation::OP_WIDEN_FLOAT;
1402 }
1403 else {
1404 fprintf(stderr, "Compiler Bug! Widening number of unknown type\n");
1405 return false;
1406 }
1407
1408 136 size_t widened_var = function->tmpvar_define(widen_to);
1409 auto operation = std::make_unique<OperationCast>(
1410 widen_type,
1411 _src_ref,
1412 widened_var,
1413 _widen_var,
1414 widen_to
1415 136 );
1416 function
1417 136 ->get_basic_block(current_block)
1418 136 .add_operation(std::move(operation));
1419 136 _widen_var = widened_var;
1420 136 return true;
1421 136 }
1422 bool
1423 244 FunctionDefinitionResolver::numeric_widen_binary_operation(
1424 const Gyoji::context::SourceReference & _src_ref,
1425 size_t & a_tmpvar,
1426 size_t & b_tmpvar,
1427 const Gyoji::mir::Type *atype,
1428 const Gyoji::mir::Type *btype,
1429 const Gyoji::mir::Type **widened
1430 )
1431 {
1432
2/2
✓ Branch 1 taken 212 times.
✓ Branch 2 taken 32 times.
244 if (atype->is_integer()) {
1433 // Deal with signed-ness. We can't mix signed and unsigned.
1434
5/6
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 112 times.
✓ Branch 4 taken 100 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 100 times.
✓ Branch 7 taken 112 times.
212 if (atype->is_signed() && btype->is_signed()) {
1435
2/2
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 70 times.
100 if (atype->get_primitive_size() > btype->get_primitive_size()) {
1436
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 if (!numeric_widen(_src_ref, b_tmpvar, atype)) {
1437 return false;
1438 }
1439 30 *widened = atype;
1440 }
1441
2/2
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 40 times.
70 else if (atype->get_primitive_size() < btype->get_primitive_size()) {
1442
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 if (!numeric_widen(_src_ref, a_tmpvar, btype)) {
1443 return false;
1444 }
1445 30 *widened = btype;
1446 }
1447 else {
1448 // Both types are the same size already, so we do nothing
1449 // and it doesn't matter whether we picked 'a' or 'b'.
1450 40 *widened = atype;
1451 }
1452 }
1453
3/6
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
112 else if (atype->is_unsigned() && btype->is_unsigned()) {
1454
2/2
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 82 times.
112 if (atype->get_primitive_size() > btype->get_primitive_size()) {
1455
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 if (!numeric_widen(_src_ref, b_tmpvar, atype)) {
1456 return false;
1457 }
1458 30 *widened = atype;
1459 }
1460
2/2
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 52 times.
82 else if (atype->get_primitive_size() < btype->get_primitive_size()) {
1461
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 if (!numeric_widen(_src_ref, a_tmpvar, btype)) {
1462 return false;
1463 }
1464 30 *widened = btype;
1465 }
1466 else {
1467 // Both types are the same size already, so we do nothing
1468 // and it doesn't matter whether we picked 'a' or 'b'.
1469 52 *widened = atype;
1470 }
1471 }
1472 else {
1473 compiler_context
1474 .get_errors()
1475 .add_simple_error(
1476 _src_ref,
1477 "Type mismatch in binary operation",
1478 std::string("The type of operands both signed or unsigned. Automatic cast from signed to unsigned is not supported. a= ") +
1479 atype->get_name() + std::string(" b=") + btype->get_name()
1480 );
1481 return false;
1482 }
1483 }
1484 // We don't have to check because we already
1485 // know that we have a float in the else.
1486 else {
1487
2/2
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 24 times.
32 if (atype->get_primitive_size() > btype->get_primitive_size()) {
1488
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (!numeric_widen(_src_ref, b_tmpvar, atype)) {
1489 return false;
1490 }
1491 8 *widened = atype;
1492 }
1493
2/2
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 16 times.
24 else if (atype->get_primitive_size() < btype->get_primitive_size()) {
1494
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (!numeric_widen(_src_ref, a_tmpvar, btype)) {
1495 return false;
1496 }
1497 8 *widened = btype;
1498 }
1499 else {
1500 16 *widened = atype;
1501 }
1502 }
1503 244 return true;
1504 }
1505
1506 bool
1507 244 FunctionDefinitionResolver::handle_binary_operation_arithmetic(
1508 const Gyoji::context::SourceReference & _src_ref,
1509 Operation::OperationType type,
1510 size_t & returned_tmpvar,
1511 size_t a_tmpvar,
1512 size_t b_tmpvar
1513 )
1514 {
1515 244 const Type *atype = function->tmpvar_get(a_tmpvar);
1516 244 const Type *btype = function->tmpvar_get(b_tmpvar);
1517 // Check that both operands are numeric.
1518
3/6
✓ Branch 1 taken 244 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 244 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 244 times.
244 if (!atype->is_numeric() || !btype->is_numeric()) {
1519 compiler_context
1520 .get_errors()
1521 .add_simple_error(
1522 _src_ref,
1523 "Type mismatch in binary operation",
1524 std::string("The type of operands should be numeric, but were: a= ") + atype->get_name() + std::string(" b=") + btype->get_name()
1525 );
1526 return false;
1527 }
1528 // Special-case for modulo because it doesn't support floating-point types.
1529
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 202 times.
244 if (type == Operation::OP_MODULO) {
1530
3/6
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 42 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 42 times.
42 if (atype->is_float() || btype->is_float()) {
1531 compiler_context
1532 .get_errors()
1533 .add_simple_error(
1534 _src_ref,
1535 "Type mismatch in binary operation",
1536 std::string("The type of operands should be integer, but were a= ") + atype->get_name() + std::string(" b=") + btype->get_name()
1537 );
1538 return false;
1539 }
1540 }
1541
1542 // The arguments must be either both integer
1543 // or both float.
1544
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 244 times.
244 if (!(
1545
3/4
✓ Branch 1 taken 212 times.
✓ Branch 2 taken 32 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 212 times.
244 (atype->is_integer() && btype->is_integer()) ||
1546
2/4
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 32 times.
32 (atype->is_float() && btype->is_float())
1547 )) {
1548 compiler_context
1549 .get_errors()
1550 .add_simple_error(
1551 _src_ref,
1552 "Type mismatch in binary operation",
1553 std::string("The type of operands both integer or floating-point types. Automatic cast from int to float is not supported. a= ") +
1554 atype->get_name() + std::string(" b=") + btype->get_name()
1555 );
1556 return false;
1557 }
1558
1559 // Now, both of them are either int or float, so we need to handle each separately
1560 // so that we can choose the appropriate cast type.
1561 244 const Type *widened = nullptr;
1562
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
244 if (!numeric_widen_binary_operation(_src_ref, a_tmpvar, b_tmpvar, atype, btype, &widened)) {
1563 return false;
1564 }
1565
1566 // The return type is whatever
1567 // we widened the add to be.
1568 244 returned_tmpvar = function->tmpvar_define(widened);
1569
1570 // We emit the appropriate operation as either an integer or
1571 // floating-point add depending on which one it was.
1572 // The back-end may assume that the operand types are both
1573 // integer or floating point of the same type and
1574 // the return type will also be of the same type.
1575 auto operation = std::make_unique<OperationBinary>(
1576 type,
1577 _src_ref,
1578 returned_tmpvar,
1579 a_tmpvar,
1580 b_tmpvar
1581 244 );
1582 function
1583 244 ->get_basic_block(current_block)
1584 244 .add_operation(std::move(operation));
1585
1586 244 return true;
1587 244 }
1588
1589 bool
1590 FunctionDefinitionResolver::handle_binary_operation_logical(
1591 const Gyoji::context::SourceReference & _src_ref,
1592 Operation::OperationType type,
1593 size_t & returned_tmpvar,
1594 size_t a_tmpvar,
1595 size_t b_tmpvar
1596 )
1597 {
1598 const Type *atype = function->tmpvar_get(a_tmpvar);
1599 const Type *btype = function->tmpvar_get(b_tmpvar);
1600 // Check that both operands are numeric.
1601 if (!atype->is_bool() || !btype->is_bool()) {
1602 compiler_context
1603 .get_errors()
1604 .add_simple_error(
1605 _src_ref,
1606 "Type mismatch in logical operation",
1607 std::string("The type of operands should be bool , but were: a= ") + atype->get_name() + std::string(" b=") + btype->get_name()
1608 );
1609 return false;
1610 }
1611 auto operation = std::make_unique<OperationBinary>(
1612 type,
1613 _src_ref,
1614 returned_tmpvar,
1615 a_tmpvar,
1616 b_tmpvar
1617 );
1618 function
1619 ->get_basic_block(current_block)
1620 .add_operation(std::move(operation));
1621 return true;
1622 }
1623
1624 bool
1625 FunctionDefinitionResolver::handle_binary_operation_bitwise(
1626 const Gyoji::context::SourceReference & _src_ref,
1627 Operation::OperationType type,
1628 size_t & returned_tmpvar,
1629 size_t a_tmpvar,
1630 size_t b_tmpvar
1631 )
1632 {
1633 const Type *atype = function->tmpvar_get(a_tmpvar);
1634 const Type *btype = function->tmpvar_get(b_tmpvar);
1635 // Check that both operands are numeric.
1636 if (!atype->is_unsigned() || !btype->is_unsigned()) {
1637 compiler_context
1638 .get_errors()
1639 .add_simple_error(
1640 _src_ref,
1641 "Type mismatch in binary operation",
1642 std::string("The type of operands should be unsigned integers, but were: a= ") + atype->get_name() + std::string(" b=") + btype->get_name()
1643 );
1644 return false;
1645 }
1646 // Now widen them to the appropriate sizes.
1647 const Type *widened = nullptr;
1648 if (!numeric_widen_binary_operation(_src_ref, a_tmpvar, b_tmpvar, atype, btype, &widened)) {
1649 return false;
1650 }
1651
1652 // The return type is whatever
1653 // we widened the add to be.
1654 returned_tmpvar = function->tmpvar_define(widened);
1655
1656 // We emit the appropriate operation as either an integer or
1657 // floating-point add depending on which one it was.
1658 // The back-end may assume that the operand types are both
1659 // integer or floating point of the same type and
1660 // the return type will also be of the same type.
1661 auto operation = std::make_unique<OperationBinary>(
1662 type,
1663 _src_ref,
1664 returned_tmpvar,
1665 a_tmpvar,
1666 b_tmpvar
1667 );
1668 function
1669 ->get_basic_block(current_block)
1670 .add_operation(std::move(operation));
1671
1672 return true;
1673 }
1674
1675
1676 bool
1677 FunctionDefinitionResolver::handle_binary_operation_shift(
1678 const Gyoji::context::SourceReference & _src_ref,
1679 Operation::OperationType type,
1680 size_t & returned_tmpvar,
1681 size_t a_tmpvar,
1682 size_t b_tmpvar
1683 )
1684 {
1685 const Type *atype = function->tmpvar_get(a_tmpvar);
1686 const Type *btype = function->tmpvar_get(b_tmpvar);
1687 // Check that both operands are numeric.
1688 if (!atype->is_unsigned() || !btype->is_unsigned()) {
1689 compiler_context
1690 .get_errors()
1691 .add_simple_error(
1692 _src_ref,
1693 "Type mismatch in binary operation",
1694 std::string("The type of operands should be unsigned integers, but were: a= ") + atype->get_name() + std::string(" b=") + btype->get_name()
1695 );
1696 return false;
1697 }
1698
1699 // Notably, we never widen a shift operation.
1700 // Instead, we operate on whatever it is because
1701 // it will end up being masked down to an 8-bit
1702 // value to avoid overflowing the LHS when the
1703 // shift happens if it's any bigger.
1704
1705 // We always return the same size that the LHS is.
1706 returned_tmpvar = function->tmpvar_define(atype);
1707
1708 // We emit the appropriate operation as either an integer or
1709 // floating-point add depending on which one it was.
1710 // The back-end may assume that the operand types are both
1711 // integer or floating point of the same type and
1712 // the return type will also be of the same type.
1713 auto operation = std::make_unique<OperationBinary>(
1714 type,
1715 _src_ref,
1716 returned_tmpvar,
1717 a_tmpvar,
1718 b_tmpvar
1719 );
1720 function
1721 ->get_basic_block(current_block)
1722 .add_operation(std::move(operation));
1723
1724 return true;
1725 }
1726
1727 bool
1728 38 FunctionDefinitionResolver::handle_binary_operation_compare(
1729 const Gyoji::context::SourceReference & _src_ref,
1730 Operation::OperationType type,
1731 size_t & returned_tmpvar,
1732 size_t a_tmpvar,
1733 size_t b_tmpvar
1734 )
1735 {
1736 38 const Type *atype = function->tmpvar_get(a_tmpvar);
1737 38 const Type *btype = function->tmpvar_get(b_tmpvar);
1738 // Check that both operands are the same type.
1739
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 38 times.
38 if (atype->get_name() != btype->get_name()) {
1740 compiler_context
1741 .get_errors()
1742 .add_simple_error(
1743 _src_ref,
1744 "Type mismatch in compare operation",
1745 std::string("The operands of a comparision should be the same type, but were: a= ") + atype->get_name() + std::string(" b=") + btype->get_name()
1746 );
1747 return false;
1748 }
1749
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
38 if (atype->is_void()) {
1750 compiler_context
1751 .get_errors()
1752 .add_simple_error(
1753 _src_ref,
1754 "Type mismatch in compare operation",
1755 std::string("The operands of a comparison must not be void, but were: a= ") + atype->get_name() + std::string(" b=") + btype->get_name()
1756 );
1757 return false;
1758 }
1759
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
38 if (atype->is_composite()) {
1760 compiler_context
1761 .get_errors()
1762 .add_simple_error(
1763 _src_ref,
1764 "Type mismatch in compare operation",
1765 std::string("The operands of a comparison must not be composite structures or classes, but were: a= ") + atype->get_name() + std::string(" b=") + btype->get_name()
1766 );
1767 return false;
1768 }
1769 38 if (
1770
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 38 times.
76 (atype->is_pointer() || atype->is_reference())
1771
2/4
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 38 times.
76 &&
1772 !(type == Operation::OP_COMPARE_EQUAL ||
1773 type == Operation::OP_COMPARE_NOT_EQUAL)
1774 ) {
1775 compiler_context
1776 .get_errors()
1777 .add_simple_error(
1778 _src_ref,
1779 "Type mismatch in compare operation",
1780 std::string("The operands of a comparison of pointers and references may not be used except for equality comparisions, but were: a= ") + atype->get_name() + std::string(" b=") + btype->get_name()
1781 );
1782 return false;
1783 }
1784
1785
1786 // Notably, we never widen a shift operation.
1787 // Instead, we operate on whatever it is because
1788 // it will end up being masked down to an 8-bit
1789 // value to avoid overflowing the LHS when the
1790 // shift happens if it's any bigger.
1791
1792 // We always return the same size that the LHS is.
1793 76 returned_tmpvar = function->tmpvar_define(mir.get_types().get_type("bool"));
1794
1795 // We emit the appropriate operation as either an integer or
1796 // floating-point add depending on which one it was.
1797 // The back-end may assume that the operand types are both
1798 // integer or floating point of the same type and
1799 // the return type will also be of the same type.
1800 auto operation = std::make_unique<OperationBinary>(
1801 type,
1802 _src_ref,
1803 returned_tmpvar,
1804 a_tmpvar,
1805 b_tmpvar
1806 38 );
1807 function
1808 38 ->get_basic_block(current_block)
1809 38 .add_operation(std::move(operation));
1810
1811 38 return true;
1812 38 }
1813
1814 bool
1815 384 FunctionDefinitionResolver::handle_binary_operation_assignment(
1816 const Gyoji::context::SourceReference & _src_ref,
1817 Operation::OperationType type,
1818 size_t & returned_tmpvar,
1819 size_t a_tmpvar,
1820 size_t b_tmpvar
1821 )
1822 {
1823 384 const Type *atype = function->tmpvar_get(a_tmpvar);
1824 384 const Type *btype = function->tmpvar_get(b_tmpvar);
1825 // Check that both operands are the same type.
1826
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 384 times.
384 if (atype->get_name() != btype->get_name()) {
1827 compiler_context
1828 .get_errors()
1829 .add_simple_error(
1830 _src_ref,
1831 "Type mismatch in assignment operation",
1832 std::string("The operands of an assignment should be the same type, but were: a= ") + atype->get_name() + std::string(" b=") + btype->get_name()
1833 );
1834 return false;
1835 }
1836
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 384 times.
384 if (atype->is_void()) {
1837 compiler_context
1838 .get_errors()
1839 .add_simple_error(
1840 _src_ref,
1841 "Type mismatch in assignment operation",
1842 std::string("The operands of an assignment must not be void, but were: a= ") + atype->get_name() + std::string(" b=") + btype->get_name()
1843 );
1844 return false;
1845 }
1846
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 384 times.
384 if (atype->is_composite()) {
1847 compiler_context
1848 .get_errors()
1849 .add_simple_error(
1850 _src_ref,
1851 "Type mismatch in assignment operation",
1852 std::string("The operands of an assignment must not be composite structures or classes, but were: a= ") + atype->get_name() + std::string(" b=") + btype->get_name()
1853 );
1854 return false;
1855 }
1856
1857 // Notably, we never widen a shift operation.
1858 // Instead, we operate on whatever it is because
1859 // it will end up being masked down to an 8-bit
1860 // value to avoid overflowing the LHS when the
1861 // shift happens if it's any bigger.
1862
1863 // We always return the same size that the LHS is.
1864 384 returned_tmpvar = function->tmpvar_define(atype);
1865
1866 // We emit the appropriate operation as either an integer or
1867 // floating-point add depending on which one it was.
1868 // The back-end may assume that the operand types are both
1869 // integer or floating point of the same type and
1870 // the return type will also be of the same type.
1871 auto operation = std::make_unique<OperationBinary>(
1872 type,
1873 _src_ref,
1874 returned_tmpvar,
1875 a_tmpvar,
1876 b_tmpvar
1877 384 );
1878 function
1879 384 ->get_basic_block(current_block)
1880 384 .add_operation(std::move(operation));
1881
1882 384 return true;
1883 384 }
1884
1885 bool
1886 658 FunctionDefinitionResolver::extract_from_expression_binary(
1887 size_t & returned_tmpvar,
1888 const ExpressionBinary & expression)
1889 {
1890 size_t a_tmpvar;
1891 size_t b_tmpvar;
1892
1893
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 658 times.
658 if (!extract_from_expression(a_tmpvar, expression.get_a())) {
1894 return false;
1895 }
1896
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 658 times.
658 if (!extract_from_expression(b_tmpvar, expression.get_b())) {
1897 return false;
1898 }
1899
1900 ExpressionBinary::OperationType op_type;
1901 658 op_type = expression.get_operator();
1902
1903
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 604 times.
658 if (op_type == ExpressionBinary::ADD) {
1904
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 54 times.
54 if (!handle_binary_operation_arithmetic(
1905 expression.get_source_ref(),
1906 Operation::OP_ADD,
1907 returned_tmpvar,
1908 a_tmpvar,
1909 b_tmpvar)) {
1910 return false;
1911 }
1912 }
1913
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 556 times.
604 else if (op_type == ExpressionBinary::SUBTRACT) {
1914
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
48 if (!handle_binary_operation_arithmetic(
1915 expression.get_source_ref(),
1916 Operation::OP_SUBTRACT,
1917 returned_tmpvar,
1918 a_tmpvar,
1919 b_tmpvar)) {
1920 return false;
1921 }
1922 }
1923
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 508 times.
556 else if (op_type == ExpressionBinary::MULTIPLY) {
1924
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
48 if (!handle_binary_operation_arithmetic(
1925 expression.get_source_ref(),
1926 Operation::OP_MULTIPLY,
1927 returned_tmpvar,
1928 a_tmpvar,
1929 b_tmpvar)) {
1930 return false;
1931 }
1932 }
1933
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 460 times.
508 else if (op_type == ExpressionBinary::DIVIDE) {
1934
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
48 if (!handle_binary_operation_arithmetic(
1935 expression.get_source_ref(),
1936 Operation::OP_DIVIDE,
1937 returned_tmpvar,
1938 a_tmpvar,
1939 b_tmpvar)) {
1940 return false;
1941 }
1942 }
1943
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 418 times.
460 else if (op_type == ExpressionBinary::MODULO) {
1944
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 42 times.
42 if (!handle_binary_operation_arithmetic(
1945 expression.get_source_ref(),
1946 Operation::OP_MODULO,
1947 returned_tmpvar,
1948 a_tmpvar,
1949 b_tmpvar)) {
1950 return false;
1951 }
1952 }
1953
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 418 times.
418 else if (op_type == ExpressionBinary::LOGICAL_AND) {
1954 if (!handle_binary_operation_logical(
1955 expression.get_source_ref(),
1956 Operation::OP_LOGICAL_AND,
1957 returned_tmpvar,
1958 a_tmpvar,
1959 b_tmpvar)) {
1960 return false;
1961 }
1962 }
1963
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 418 times.
418 else if (op_type == ExpressionBinary::LOGICAL_OR) {
1964 if (!handle_binary_operation_logical(
1965 expression.get_source_ref(),
1966 Operation::OP_LOGICAL_OR,
1967 returned_tmpvar,
1968 a_tmpvar,
1969 b_tmpvar)) {
1970 return false;
1971 }
1972 }
1973
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 418 times.
418 else if (op_type == ExpressionBinary::BITWISE_AND) {
1974 if (!handle_binary_operation_bitwise(
1975 expression.get_source_ref(),
1976 Operation::OP_BITWISE_AND,
1977 returned_tmpvar,
1978 a_tmpvar,
1979 b_tmpvar)) {
1980 return false;
1981 }
1982 }
1983
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 418 times.
418 else if (op_type == ExpressionBinary::BITWISE_OR) {
1984 if (!handle_binary_operation_bitwise(
1985 expression.get_source_ref(),
1986 Operation::OP_BITWISE_OR,
1987 returned_tmpvar,
1988 a_tmpvar,
1989 b_tmpvar
1990 )) {
1991 return false;
1992 }
1993 }
1994
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 418 times.
418 else if (op_type == ExpressionBinary::BITWISE_XOR) {
1995 if (!handle_binary_operation_bitwise(
1996 expression.get_source_ref(),
1997 Operation::OP_BITWISE_XOR,
1998 returned_tmpvar,
1999 a_tmpvar,
2000 b_tmpvar
2001 )) {
2002 return false;
2003 }
2004 }
2005
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 418 times.
418 else if (op_type == ExpressionBinary::SHIFT_LEFT) {
2006 if (!handle_binary_operation_shift(
2007 expression.get_source_ref(),
2008 Operation::OP_SHIFT_LEFT,
2009 returned_tmpvar,
2010 a_tmpvar,
2011 b_tmpvar
2012 )) {
2013 return false;
2014 }
2015 }
2016
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 418 times.
418 else if (op_type == ExpressionBinary::SHIFT_RIGHT) {
2017 if (!handle_binary_operation_shift(
2018 expression.get_source_ref(),
2019 Operation::OP_SHIFT_RIGHT,
2020 returned_tmpvar,
2021 a_tmpvar,
2022 b_tmpvar
2023 )) {
2024 return false;
2025 }
2026 }
2027
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 412 times.
418 else if (op_type == ExpressionBinary::COMPARE_LESS) {
2028
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if (!handle_binary_operation_compare(
2029 expression.get_source_ref(),
2030 Operation::OP_COMPARE_LESS,
2031 returned_tmpvar,
2032 a_tmpvar,
2033 b_tmpvar
2034 )) {
2035 return false;
2036 }
2037 }
2038
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 412 times.
412 else if (op_type == ExpressionBinary::COMPARE_GREATER) {
2039 if (!handle_binary_operation_compare(
2040 expression.get_source_ref(),
2041 Operation::OP_COMPARE_GREATER,
2042 returned_tmpvar,
2043 a_tmpvar,
2044 b_tmpvar
2045 )) {
2046 return false;
2047 }
2048 }
2049
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 412 times.
412 else if (op_type == ExpressionBinary::COMPARE_LESS_EQUAL) {
2050 if (!handle_binary_operation_compare(
2051 expression.get_source_ref(),
2052 Operation::OP_COMPARE_LESS_EQUAL,
2053 returned_tmpvar,
2054 a_tmpvar,
2055 b_tmpvar
2056 )) {
2057 return false;
2058 }
2059 }
2060
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 412 times.
412 else if (op_type == ExpressionBinary::COMPARE_GREATER_EQUAL) {
2061 if (!handle_binary_operation_compare(
2062 expression.get_source_ref(),
2063 Operation::OP_COMPARE_GREATER_EQUAL,
2064 returned_tmpvar,
2065 a_tmpvar,
2066 b_tmpvar
2067 )) {
2068 return false;
2069 }
2070 }
2071
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 398 times.
412 else if (op_type == ExpressionBinary::COMPARE_EQUAL) {
2072
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
14 if (!handle_binary_operation_compare(
2073 expression.get_source_ref(),
2074 Operation::OP_COMPARE_EQUAL,
2075 returned_tmpvar,
2076 a_tmpvar,
2077 b_tmpvar
2078 )) {
2079 return false;
2080 }
2081 }
2082
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 380 times.
398 else if (op_type == ExpressionBinary::COMPARE_NOT_EQUAL) {
2083
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
18 if (!handle_binary_operation_compare(
2084 expression.get_source_ref(),
2085 Operation::OP_COMPARE_NOT_EQUAL,
2086 returned_tmpvar,
2087 a_tmpvar,
2088 b_tmpvar
2089 )) {
2090 return false;
2091 }
2092 }
2093
2/2
✓ Branch 0 taken 376 times.
✓ Branch 1 taken 4 times.
380 else if (op_type == ExpressionBinary::ASSIGNMENT) {
2094
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 376 times.
376 if (!handle_binary_operation_assignment(
2095 expression.get_source_ref(),
2096 Operation::OP_ASSIGN,
2097 returned_tmpvar,
2098 a_tmpvar,
2099 b_tmpvar
2100 )) {
2101 return false;
2102 }
2103 }
2104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 else if (op_type == ExpressionBinary::MUL_ASSIGNMENT) {
2105 // This is just syntax sugar for * followed by =
2106 size_t arithmetic_tmpvar;
2107 if (!handle_binary_operation_arithmetic(
2108 expression.get_source_ref(),
2109 Operation::OP_MULTIPLY,
2110 arithmetic_tmpvar,
2111 a_tmpvar,
2112 b_tmpvar
2113 )) {
2114 return false;
2115 }
2116 if (!handle_binary_operation_assignment(
2117 expression.get_source_ref(),
2118 Operation::OP_ASSIGN,
2119 returned_tmpvar,
2120 a_tmpvar,
2121 arithmetic_tmpvar
2122 )) {
2123 return false;
2124 }
2125 }
2126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 else if (op_type == ExpressionBinary::DIV_ASSIGNMENT) {
2127 // This is just syntax sugar for / followed by =
2128 size_t arithmetic_tmpvar;
2129 if (!handle_binary_operation_arithmetic(
2130 expression.get_source_ref(),
2131 Operation::OP_DIVIDE,
2132 arithmetic_tmpvar,
2133 a_tmpvar,
2134 b_tmpvar
2135 )) {
2136 return false;
2137 }
2138 if (!handle_binary_operation_assignment(
2139 expression.get_source_ref(),
2140 Operation::OP_ASSIGN,
2141 returned_tmpvar,
2142 a_tmpvar,
2143 arithmetic_tmpvar
2144 )) {
2145 return false;
2146 }
2147 }
2148
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 else if (op_type == ExpressionBinary::MOD_ASSIGNMENT) {
2149 // This is just syntax sugar for % followed by =
2150 size_t arithmetic_tmpvar;
2151 if (!handle_binary_operation_arithmetic(
2152 expression.get_source_ref(),
2153 Operation::OP_MODULO,
2154 arithmetic_tmpvar,
2155 a_tmpvar,
2156 b_tmpvar
2157 )) {
2158 return false;
2159 }
2160 if (!handle_binary_operation_assignment(
2161 expression.get_source_ref(),
2162 Operation::OP_ASSIGN,
2163 returned_tmpvar,
2164 a_tmpvar,
2165 arithmetic_tmpvar
2166 )) {
2167 return false;
2168 }
2169 }
2170
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 else if (op_type == ExpressionBinary::ADD_ASSIGNMENT) {
2171 // This is just syntax sugar for + followed by =
2172 size_t arithmetic_tmpvar;
2173
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (!handle_binary_operation_arithmetic(
2174 expression.get_source_ref(),
2175 Operation::OP_ADD,
2176 arithmetic_tmpvar,
2177 a_tmpvar,
2178 b_tmpvar
2179 )) {
2180 return false;
2181 }
2182
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (!handle_binary_operation_assignment(
2183 expression.get_source_ref(),
2184 Operation::OP_ASSIGN,
2185 returned_tmpvar,
2186 a_tmpvar,
2187 arithmetic_tmpvar
2188 )) {
2189 return false;
2190 }
2191 }
2192 else if (op_type == ExpressionBinary::SUB_ASSIGNMENT) {
2193 // This is just syntax sugar for - followed by =
2194 size_t arithmetic_tmpvar;
2195 if (!handle_binary_operation_arithmetic(
2196 expression.get_source_ref(),
2197 Operation::OP_SUBTRACT,
2198 arithmetic_tmpvar,
2199 a_tmpvar,
2200 b_tmpvar
2201 )) {
2202 return false;
2203 }
2204 if (!handle_binary_operation_assignment(
2205 expression.get_source_ref(),
2206 Operation::OP_ASSIGN,
2207 returned_tmpvar,
2208 a_tmpvar,
2209 arithmetic_tmpvar
2210 )) {
2211 return false;
2212 }
2213 }
2214 else if (op_type == ExpressionBinary::LEFT_ASSIGNMENT) {
2215 // This is just syntax sugar for << followed by =
2216 size_t arithmetic_tmpvar;
2217 if (!handle_binary_operation_shift(
2218 expression.get_source_ref(),
2219 Operation::OP_SHIFT_LEFT,
2220 arithmetic_tmpvar,
2221 a_tmpvar,
2222 b_tmpvar
2223 )) {
2224 return false;
2225 }
2226 if (!handle_binary_operation_assignment(
2227 expression.get_source_ref(),
2228 Operation::OP_ASSIGN,
2229 returned_tmpvar,
2230 a_tmpvar,
2231 arithmetic_tmpvar
2232 )) {
2233 return false;
2234 }
2235 }
2236 else if (op_type == ExpressionBinary::RIGHT_ASSIGNMENT) {
2237 // This is just syntax sugar for >> followed by =
2238 size_t arithmetic_tmpvar;
2239 if (!handle_binary_operation_shift(
2240 expression.get_source_ref(),
2241 Operation::OP_SHIFT_RIGHT,
2242 arithmetic_tmpvar,
2243 a_tmpvar,
2244 b_tmpvar
2245 )) {
2246 return false;
2247 }
2248 if (!handle_binary_operation_assignment(
2249 expression.get_source_ref(),
2250 Operation::OP_ASSIGN,
2251 returned_tmpvar,
2252 a_tmpvar,
2253 arithmetic_tmpvar
2254 )) {
2255 return false;
2256 }
2257 }
2258 else if (op_type == ExpressionBinary::AND_ASSIGNMENT) {
2259 // This is just syntax sugar for & followed by =
2260 size_t arithmetic_tmpvar;
2261 if (!handle_binary_operation_bitwise(
2262 expression.get_source_ref(),
2263 Operation::OP_BITWISE_AND,
2264 arithmetic_tmpvar,
2265 a_tmpvar,
2266 b_tmpvar
2267 )) {
2268 return false;
2269 }
2270 if (!handle_binary_operation_assignment(
2271 expression.get_source_ref(),
2272 Operation::OP_ASSIGN,
2273 returned_tmpvar,
2274 a_tmpvar,
2275 arithmetic_tmpvar
2276 )) {
2277 return false;
2278 }
2279 }
2280 else if (op_type == ExpressionBinary::OR_ASSIGNMENT) {
2281 // This is just syntax sugar for | followed by =
2282 size_t arithmetic_tmpvar;
2283 if (!handle_binary_operation_bitwise(
2284 expression.get_source_ref(),
2285 Operation::OP_BITWISE_OR,
2286 arithmetic_tmpvar,
2287 a_tmpvar,
2288 b_tmpvar
2289 )) {
2290 return false;
2291 }
2292 if (!handle_binary_operation_assignment(
2293 expression.get_source_ref(),
2294 Operation::OP_ASSIGN,
2295 returned_tmpvar,
2296 a_tmpvar,
2297 arithmetic_tmpvar
2298 )) {
2299 return false;
2300 }
2301 }
2302 else if (op_type == ExpressionBinary::XOR_ASSIGNMENT) {
2303 // This is just syntax sugar for ^ followed by =
2304 size_t arithmetic_tmpvar;
2305 if (!handle_binary_operation_bitwise(
2306 expression.get_source_ref(),
2307 Operation::OP_BITWISE_XOR,
2308 arithmetic_tmpvar,
2309 a_tmpvar,
2310 b_tmpvar
2311 )) {
2312 return false;
2313 }
2314 if (!handle_binary_operation_assignment(
2315 expression.get_source_ref(),
2316 Operation::OP_ASSIGN,
2317 returned_tmpvar,
2318 a_tmpvar,
2319 arithmetic_tmpvar
2320 )) {
2321 return false;
2322 }
2323 }
2324 else {
2325 compiler_context
2326 .get_errors()
2327 .add_simple_error(
2328 expression.get_source_ref(),
2329 "Compiler bug! unknown binary operator",
2330 std::string("Invalid binary operator type ") + std::to_string(op_type)
2331 );
2332 return false;
2333 }
2334 658 return true;
2335 }
2336 bool
2337 FunctionDefinitionResolver::extract_from_expression_trinary(
2338 size_t & returned_tmpvar,
2339 const ExpressionTrinary & expression)
2340 {
2341 // function
2342 // ->get_basic_block(current_block)
2343 // .add_operation(std::string("trinary operator "));
2344 return false;
2345 }
2346 bool
2347 FunctionDefinitionResolver::extract_from_expression_cast(
2348 size_t & returned_tmpvar,
2349 const ExpressionCast & expression)
2350 {
2351 // function
2352 // ->get_basic_block(current_block)
2353 // .add_operation(std::string("cast"));
2354 return false;
2355 }
2356
2357
2358 bool
2359 2266 FunctionDefinitionResolver::extract_from_expression(
2360 size_t & returned_tmpvar,
2361 const Expression & expression_container)
2362 {
2363 2266 const auto & expression_type = expression_container.get_expression();
2364
2365
2/2
✓ Branch 1 taken 1242 times.
✓ Branch 2 taken 1024 times.
2266 if (std::holds_alternative<Gyoji::owned<ExpressionPrimaryIdentifier>>(expression_type)) {
2366 1242 const auto & expression = std::get<Gyoji::owned<ExpressionPrimaryIdentifier>>(expression_type);
2367 1242 return extract_from_expression_primary_identifier(returned_tmpvar, *expression);
2368 }
2369
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1020 times.
1024 else if (std::holds_alternative<Gyoji::owned<ExpressionPrimaryNested>>(expression_type)) {
2370 4 const auto & expression = std::get<Gyoji::owned<ExpressionPrimaryNested>>(expression_type);
2371 4 return extract_from_expression_primary_nested(returned_tmpvar, *expression);
2372 }
2373
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1012 times.
1020 else if (std::holds_alternative<Gyoji::owned<ExpressionPrimaryLiteralChar>>(expression_type)) {
2374 8 const auto & expression = std::get<Gyoji::owned<ExpressionPrimaryLiteralChar>>(expression_type);
2375 8 return extract_from_expression_primary_literal_char(returned_tmpvar, *expression);
2376 }
2377
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 1002 times.
1012 else if (std::holds_alternative<Gyoji::owned<ExpressionPrimaryLiteralString>>(expression_type)) {
2378 10 const auto & expression = std::get<Gyoji::owned<ExpressionPrimaryLiteralString>>(expression_type);
2379 10 return extract_from_expression_primary_literal_string(returned_tmpvar, *expression);
2380 }
2381
2/2
✓ Branch 1 taken 220 times.
✓ Branch 2 taken 782 times.
1002 else if (std::holds_alternative<Gyoji::owned<ExpressionPrimaryLiteralInt>>(expression_type)) {
2382 220 const auto & expression = std::get<Gyoji::owned<ExpressionPrimaryLiteralInt>>(expression_type);
2383 220 return extract_from_expression_primary_literal_int(returned_tmpvar, *expression);
2384 }
2385
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 776 times.
782 else if (std::holds_alternative<Gyoji::owned<ExpressionPrimaryLiteralFloat>>(expression_type)) {
2386 6 const auto & expression = std::get<Gyoji::owned<ExpressionPrimaryLiteralFloat>>(expression_type);
2387 6 return extract_from_expression_primary_literal_float(returned_tmpvar, *expression);
2388 }
2389
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 766 times.
776 else if (std::holds_alternative<Gyoji::owned<ExpressionPostfixArrayIndex>>(expression_type)) {
2390 10 const auto & expression = std::get<Gyoji::owned<ExpressionPostfixArrayIndex>>(expression_type);
2391 10 return extract_from_expression_postfix_array_index(returned_tmpvar, *expression);
2392 }
2393
2/2
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 700 times.
766 else if (std::holds_alternative<Gyoji::owned<ExpressionPostfixFunctionCall>>(expression_type)) {
2394 66 const auto & expression = std::get<Gyoji::owned<ExpressionPostfixFunctionCall>>(expression_type);
2395 66 return extract_from_expression_postfix_function_call(returned_tmpvar, *expression);
2396 }
2397
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 690 times.
700 else if (std::holds_alternative<Gyoji::owned<ExpressionPostfixDot>>(expression_type)) {
2398 10 const auto & expression = std::get<Gyoji::owned<ExpressionPostfixDot>>(expression_type);
2399 10 return extract_from_expression_postfix_dot(returned_tmpvar, *expression);
2400 }
2401
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 688 times.
690 else if (std::holds_alternative<Gyoji::owned<ExpressionPostfixArrow>>(expression_type)) {
2402 2 const auto & expression = std::get<Gyoji::owned<ExpressionPostfixArrow>>(expression_type);
2403 2 return extract_from_expression_postfix_arrow(returned_tmpvar, *expression);
2404 }
2405
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 686 times.
688 else if (std::holds_alternative<Gyoji::owned<ExpressionPostfixIncDec>>(expression_type)) {
2406 2 const auto & expression = std::get<Gyoji::owned<ExpressionPostfixIncDec>>(expression_type);
2407 2 return extract_from_expression_postfix_incdec(returned_tmpvar, *expression);
2408 }
2409
2/2
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 666 times.
686 else if (std::holds_alternative<Gyoji::owned<ExpressionUnaryPrefix>>(expression_type)) {
2410 20 const auto & expression = std::get<Gyoji::owned<ExpressionUnaryPrefix>>(expression_type);
2411 20 return extract_from_expression_unary_prefix(returned_tmpvar, *expression);
2412 }
2413
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 658 times.
666 else if (std::holds_alternative<Gyoji::owned<ExpressionUnarySizeofType>>(expression_type)) {
2414 8 const auto & expression = std::get<Gyoji::owned<ExpressionUnarySizeofType>>(expression_type);
2415 8 return extract_from_expression_unary_sizeof_type(returned_tmpvar, *expression);
2416 }
2417
1/2
✓ Branch 1 taken 658 times.
✗ Branch 2 not taken.
658 else if (std::holds_alternative<Gyoji::owned<ExpressionBinary>>(expression_type)) {
2418 658 const auto & expression = std::get<Gyoji::owned<ExpressionBinary>>(expression_type);
2419 658 return extract_from_expression_binary(returned_tmpvar, *expression);
2420 }
2421 else if (std::holds_alternative<Gyoji::owned<ExpressionTrinary>>(expression_type)) {
2422 const auto & expression = std::get<Gyoji::owned<ExpressionTrinary>>(expression_type);
2423 return extract_from_expression_trinary(returned_tmpvar, *expression);
2424 }
2425 else if (std::holds_alternative<Gyoji::owned<ExpressionCast>>(expression_type)) {
2426 const auto & expression = std::get<Gyoji::owned<ExpressionCast>>(expression_type);
2427 return extract_from_expression_cast(returned_tmpvar, *expression);
2428 }
2429 else {
2430 fprintf(stderr, "Compiler bug, invalid expression type\n");
2431 return false;
2432 }
2433 }
2434
2435 bool
2436 310 FunctionDefinitionResolver::local_declare_or_error(
2437 const Gyoji::mir::Type *mir_type,
2438 const std::string & name,
2439 const SourceReference & source_ref
2440 )
2441 {
2442 310 scope_tracker.add_variable(name, mir_type, source_ref);
2443
2444 auto operation = std::make_unique<OperationLocalDeclare>(
2445 source_ref,
2446 name,
2447 mir_type
2448 310 );
2449 310 function->get_basic_block(current_block).add_operation(std::move(operation));
2450
2451 620 return true;
2452 310 }
2453
2454 bool
2455 308 FunctionDefinitionResolver::extract_from_statement_variable_declaration(
2456 const StatementVariableDeclaration & statement
2457 )
2458 {
2459 // TODO:
2460 // For arrays, it is at this point that we define a type
2461 // for the 'array' so we can make it the correct size based on the literal given
2462 // for the 'opt_array' stuff.
2463
2464 // Once the variable exists, we can start performing the initialization
2465 // and assigning the value to something.
2466 308 const Gyoji::mir::Type * mir_type = type_resolver.extract_from_type_specifier(statement.get_type_specifier());
2467
2468 308 fprintf(stderr, "Defining local variable %s\n", statement.get_identifier().get_name().c_str());
2469 616 if (!local_declare_or_error(
2470 mir_type,
2471
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 308 times.
616 statement.get_identifier().get_name(),
2472 308 statement.get_identifier().get_source_ref()
2473 )) {
2474 return false;
2475 }
2476
2477 308 const auto & initializer_expression = statement.get_initializer_expression();
2478
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 304 times.
308 if (initializer_expression.has_expression()) {
2479 // From here, we need to:
2480 // 1) call LocalVariable
2481 // 2) Evaluate the expression
2482 // 3) Perform an assignment.
2483 4 size_t variable_tmpvar = function->tmpvar_define(mir_type);
2484 auto operation = std::make_unique<OperationLocalVariable>(
2485 statement.get_source_ref(),
2486 variable_tmpvar,
2487 8 statement.get_identifier().get_name(),
2488 mir_type
2489 4 );
2490 4 function->get_basic_block(current_block).add_operation(std::move(operation));
2491
2492 size_t initial_value_tmpvar;
2493
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (!extract_from_expression(initial_value_tmpvar, initializer_expression.get_expression())) {
2494 return false;
2495 }
2496
2497 size_t returned_tmpvar; // We don't save the returned val because nobody wants it.
2498
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (!handle_binary_operation_assignment(
2499 initializer_expression.get_source_ref(),
2500 Operation::OP_ASSIGN,
2501 returned_tmpvar,
2502 variable_tmpvar,
2503 initial_value_tmpvar
2504 )) {
2505 return false;
2506 }
2507
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 }
2508 else {
2509 // TODO: In order to avoid undefined behavior, we should
2510 // always make sure that the variable has an initial default
2511 // value even if there isn't one provided. We should look
2512 // at the type system and ask it for a 'default' value
2513 // for the type.
2514 // In many cases, this would be the constructor if it
2515 // is available for that type.
2516 }
2517
2518
2519 308 return true;
2520 }
2521
2522 bool
2523 34 FunctionDefinitionResolver::extract_from_statement_ifelse(
2524 const StatementIfElse & statement
2525 )
2526 {
2527 size_t condition_tmpvar;
2528
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 34 times.
34 if (!extract_from_expression(condition_tmpvar, statement.get_expression())) {
2529 return false;
2530 }
2531 // First, evaluate the expression to get our condition.
2532
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 34 times.
34 if (!function->tmpvar_get(condition_tmpvar)->is_bool()) {
2533 compiler_context
2534 .get_errors()
2535 .add_simple_error(
2536 statement.get_expression().get_source_ref(),
2537 "Invalid condition in if statement.",
2538 std::string("Type of condition expression should be 'bool' and was ") + function->tmpvar_get(condition_tmpvar)->get_name()
2539 );
2540 return false;
2541 }
2542
2543 34 size_t blockid_if = function->add_block();
2544 34 size_t blockid_done = function->add_block();
2545 34 size_t blockid_else = -1;
2546
5/6
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 26 times.
✓ Branch 6 taken 8 times.
✓ Branch 7 taken 26 times.
34 if (statement.has_else() || statement.has_else_if()) {
2547 // If we have an 'else', then
2548 // dump to it based on the condition.
2549 8 blockid_else = function->add_block();
2550 auto operation = std::make_unique<OperationJumpConditional>(
2551 statement.get_source_ref(),
2552 condition_tmpvar,
2553 blockid_if,
2554 blockid_else
2555 8 );
2556 8 function->get_basic_block(current_block).add_operation(std::move(operation));
2557 8 }
2558 else {
2559 // Otherwise, jump to done
2560 // based on condition.
2561 auto operation = std::make_unique<OperationJumpConditional>(
2562 statement.get_source_ref(),
2563 condition_tmpvar,
2564 blockid_if,
2565 blockid_done
2566 26 );
2567 26 function->get_basic_block(current_block).add_operation(std::move(operation));
2568 26 }
2569
2570 34 current_block = blockid_if;
2571
2572 // Perform the stuff inside the 'if' block.
2573 34 scope_tracker.scope_push(statement.get_if_scope_body().get_source_ref());
2574
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 34 times.
34 if (!extract_from_statement_list(
2575 34 statement.get_if_scope_body().get_statements()
2576 )) {
2577 return false;
2578 }
2579 34 scope_tracker.scope_pop();
2580
2581 // Unconditionally jump to done
2582 // unless the block is alreayd terminated by
2583 // another jump or a return.
2584
2/2
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 18 times.
34 if (!function->get_basic_block(current_block).contains_terminator()) {
2585 auto operation = std::make_unique<OperationJump>(
2586 statement.get_source_ref(),
2587 blockid_done
2588 16 );
2589 16 function->get_basic_block(current_block).add_operation(std::move(operation));
2590 16 }
2591
2592
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 26 times.
34 if (statement.has_else()) {
2593 // Perform the stuff in the 'else' block.
2594 8 scope_tracker.scope_push(statement.get_else_scope_body().get_source_ref());
2595 8 size_t blockid_tmp = current_block;
2596 8 current_block = blockid_else;
2597
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if (!extract_from_statement_list(
2598 8 statement.get_else_scope_body().get_statements()
2599 )) {
2600 return false;
2601 }
2602 8 current_block = blockid_tmp;
2603 8 scope_tracker.scope_pop();
2604 // Jump to the 'done' block when the 'else' block is finished
2605 // unless it has terminated already.
2606
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 if (!function->get_basic_block(blockid_else).contains_terminator()) {
2607 auto operation = std::make_unique<OperationJump>(
2608 statement.get_source_ref(),
2609 blockid_done
2610 8 );
2611 8 function->get_basic_block(blockid_else).add_operation(std::move(operation));
2612 8 }
2613 }
2614
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 else if (statement.has_else_if()) {
2615 if (!extract_from_statement_ifelse(
2616 statement.get_else_if()
2617 )) {
2618 return false;
2619 }
2620 }
2621 34 current_block = blockid_done;
2622 34 return true;
2623 }
2624
2625 bool
2626 2 FunctionDefinitionResolver::extract_from_statement_while(
2627 const Gyoji::frontend::tree::StatementWhile & statement
2628 )
2629 {
2630 size_t condition_tmpvar;
2631
2632 2 size_t blockid_evaluate_expression = function->add_block();
2633 2 size_t blockid_if = function->add_block();
2634 2 size_t blockid_done = function->add_block();
2635
2636 auto operation_jump_initial = std::make_unique<OperationJump>(
2637 statement.get_source_ref(),
2638 blockid_evaluate_expression
2639 2 );
2640 2 function->get_basic_block(current_block).add_operation(std::move(operation_jump_initial));
2641
2642 2 current_block = blockid_evaluate_expression;
2643
2644
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_expression(condition_tmpvar, statement.get_expression())) {
2645 return false;
2646 }
2647
2648 auto operation_jump_conditional = std::make_unique<OperationJumpConditional>(
2649 statement.get_source_ref(),
2650 condition_tmpvar,
2651 blockid_if,
2652 blockid_done
2653 2 );
2654 2 function->get_basic_block(current_block).add_operation(std::move(operation_jump_conditional));
2655
2656 // Push a loop scope.
2657 2 current_block = blockid_if;
2658 2 scope_tracker.scope_push_loop(
2659 2 statement.get_scope_body().get_source_ref(),
2660 blockid_done,
2661 blockid_evaluate_expression
2662 );
2663
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_statement_list(
2664 2 statement.get_scope_body().get_statements()
2665 )) {
2666 return false;
2667 }
2668
2669 // Pop back from the scope.
2670 2 scope_tracker.scope_pop();
2671
2672 auto operation_jump_to_evaluate = std::make_unique<OperationJump>(
2673 statement.get_source_ref(),
2674 blockid_evaluate_expression
2675 2 );
2676 2 function->get_basic_block(current_block).add_operation(std::move(operation_jump_to_evaluate));
2677
2678 2 current_block = blockid_done;
2679
2680 2 return true;
2681 2 }
2682
2683 bool
2684 2 FunctionDefinitionResolver::extract_from_statement_for(
2685 const Gyoji::frontend::tree::StatementFor & statement
2686 )
2687 {
2688 size_t condition_tmpvar;
2689
2690 2 size_t blockid_evaluate_expression_termination = function->add_block();
2691 2 size_t blockid_if = function->add_block();
2692 2 size_t blockid_done = function->add_block();
2693
2694
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (statement.is_declaration()) {
2695 2 const Gyoji::mir::Type * mir_type = type_resolver.extract_from_type_specifier(statement.get_type_specifier());
2696
2697 4 if (!local_declare_or_error(
2698 mir_type,
2699
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
4 statement.get_identifier().get_name(),
2700 2 statement.get_identifier().get_source_ref()
2701 )) {
2702 return false;
2703 }
2704 }
2705
2706 // Evaluate the initialization expression
2707
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_expression(condition_tmpvar, statement.get_expression_initial())) {
2708 return false;
2709 }
2710
2711 auto operation_jump_initial = std::make_unique<OperationJump>(
2712 statement.get_source_ref(),
2713 blockid_evaluate_expression_termination
2714 2 );
2715 2 function->get_basic_block(current_block).add_operation(std::move(operation_jump_initial));
2716
2717 // Evaluate the termination condition.
2718 2 size_t blockid_tmp = current_block;
2719 2 current_block = blockid_evaluate_expression_termination;
2720
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_expression(condition_tmpvar, statement.get_expression_termination())) {
2721 return false;
2722 }
2723 2 current_block = blockid_tmp;
2724
2725 auto operation_jump_conditional = std::make_unique<OperationJumpConditional>(
2726 statement.get_source_ref(),
2727 condition_tmpvar,
2728 blockid_if,
2729 blockid_done
2730 2 );
2731 2 function->get_basic_block(blockid_evaluate_expression_termination).add_operation(std::move(operation_jump_conditional));
2732
2733 2 scope_tracker.scope_push_loop(
2734 2 statement.get_scope_body().get_source_ref(),
2735 blockid_done,
2736 blockid_evaluate_expression_termination
2737 );
2738
2739 2 size_t blockid_tmp_if = current_block;
2740 2 current_block = blockid_if;
2741
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_statement_list(
2742 2 statement.get_scope_body().get_statements()
2743 )) {
2744 return false;
2745 }
2746
2747 2 scope_tracker.scope_pop();
2748
2749 // Evaluate the 'increment' expression
2750
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_expression(condition_tmpvar, statement.get_expression_increment())) {
2751 return false;
2752 }
2753 2 current_block = blockid_tmp_if;
2754
2755 auto operation_jump_to_evaluate = std::make_unique<OperationJump>(
2756 statement.get_source_ref(),
2757 blockid_evaluate_expression_termination
2758 2 );
2759 2 function->get_basic_block(blockid_if).add_operation(std::move(operation_jump_to_evaluate));
2760
2761 2 current_block = blockid_done;
2762
2763 // Un-declare the variable we declared.
2764 // leave_scope(function, current_block, statement.get_source_ref(), unwind);
2765
2766 2 return true;
2767 2 }
2768
2769
2770
2771 bool
2772 2 FunctionDefinitionResolver::extract_from_statement_break(
2773 const Gyoji::frontend::tree::StatementBreak & statement
2774 )
2775 {
2776
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!scope_tracker.is_in_loop()) {
2777 compiler_context
2778 .get_errors()
2779 .add_simple_error(statement.get_source_ref(),
2780 "'break' statement not in loop or switch statement",
2781 "'break' keyword must appear inside a loop (for/while)"
2782 );
2783 return true;
2784 }
2785
2786 2 std::vector<std::string> unwind_break = scope_tracker.get_variables_to_unwind_for_break();
2787 2 leave_scope(statement.get_source_ref(), unwind_break);
2788
2789 auto operation_jump_to_break = std::make_unique<OperationJump>(
2790 statement.get_source_ref(),
2791 2 scope_tracker.get_loop_break_blockid()
2792 2 );
2793 2 function->get_basic_block(current_block).add_operation(std::move(operation_jump_to_break));
2794 2 current_block = function->add_block();
2795
2796 2 return true;
2797 2 }
2798
2799 bool
2800 2 FunctionDefinitionResolver::extract_from_statement_continue(
2801 const Gyoji::frontend::tree::StatementContinue & statement
2802 )
2803 {
2804
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!scope_tracker.is_in_loop()) {
2805 compiler_context
2806 .get_errors()
2807 .add_simple_error(statement.get_source_ref(),
2808 "'continue' statement not in loop or switch statement",
2809 "'continue' keyword must appear inside a loop (for/while)"
2810 );
2811 return true;
2812 }
2813 auto operation_jump_to_continue = std::make_unique<OperationJump>(
2814 statement.get_source_ref(),
2815 2 scope_tracker.get_loop_continue_blockid()
2816 2 );
2817 2 function->get_basic_block(current_block).add_operation(std::move(operation_jump_to_continue));
2818 2 current_block = function->add_block();
2819
2820 2 return true;
2821 2 }
2822
2823
2824 bool
2825 2 FunctionDefinitionResolver::extract_from_statement_label(
2826 const Gyoji::frontend::tree::StatementLabel & statement
2827 )
2828 {
2829 // We're starting a new label, so this is, by definition,
2830 // a new basic block. This means we need to create a new
2831 // block and issue a 'jump' to it.
2832
2833 2 const std::string & label_name = statement.get_name();
2834 size_t label_block;
2835
2836 2 const FunctionLabel *label = scope_tracker.get_label(label_name);
2837
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (label == nullptr) {
2838 2 label_block = function->add_block();
2839 2 scope_tracker.label_define(label_name, label_block, statement.get_name_source_ref());
2840 }
2841 else {
2842 if (!label->is_resolved()) {
2843 scope_tracker.label_define(label_name, statement.get_name_source_ref());
2844 label_block = label->get_block();
2845 }
2846 else {
2847 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Labels in functions must be unique");
2848 error->add_message(statement.get_name_source_ref(),
2849 std::string("Duplicate label ") + label_name);
2850 error->add_message(label->get_source_ref(),
2851 "First declared here.");
2852 compiler_context
2853 .get_errors()
2854 .add_error(std::move(error));
2855 return true;
2856 }
2857 }
2858 auto operation = std::make_unique<OperationJump>(
2859 statement.get_source_ref(),
2860 label_block
2861 2 );
2862 2 function->get_basic_block(current_block).add_operation(std::move(operation));
2863 // Then whatever we add next will be in this new block.
2864 2 current_block = label_block;
2865
2866 2 return true;
2867 2 }
2868
2869 bool
2870 2 FunctionDefinitionResolver::extract_from_statement_goto(
2871 const Gyoji::frontend::tree::StatementGoto & statement
2872 )
2873 {
2874 2 const std::string & label_name = statement.get_label();
2875
2876 2 const FunctionLabel *label = scope_tracker.get_label(label_name);
2877 size_t label_block;
2878
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (label == nullptr) {
2879 label_block = function->add_block();
2880 scope_tracker.label_declare(label_name, label_block);
2881 }
2882 else {
2883 2 label_block = label->get_block();
2884 }
2885
2886 // We need to track what point
2887 // in the MIR the goto appears so that
2888 // we can insert the unwindings before that point.
2889 2 Gyoji::owned<FunctionPoint> function_point = std::make_unique<FunctionPoint>(current_block, function->get_basic_block(current_block).size());
2890 2 scope_tracker.add_goto(label_name, std::move(function_point), statement.get_label_source_ref());
2891
2892 auto operation = std::make_unique<OperationJump>(
2893 statement.get_source_ref(),
2894 label_block
2895 2 );
2896 2 function->get_basic_block(current_block).add_operation(std::move(operation));
2897 // This jump ends the basic block, so we start a new one.
2898 2 size_t next_block = function->add_block();
2899 2 current_block = next_block;
2900
2901 4 return true;
2902 2 }
2903
2904 bool
2905 284 FunctionDefinitionResolver::extract_from_statement_return(
2906 const StatementReturn & statement
2907 )
2908 {
2909 284 std::vector<std::string> unwind_root = scope_tracker.get_variables_to_unwind_for_root();
2910
2911
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 282 times.
284 if (statement.is_void()) {
2912 2 leave_scope(statement.get_source_ref(), unwind_root);
2913 auto operation = std::make_unique<OperationReturnVoid>(
2914 statement.get_source_ref()
2915 2 );
2916 2 function->get_basic_block(current_block).add_operation(std::move(operation));
2917 2 }
2918 else {
2919 size_t expression_tmpvar;
2920
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 282 times.
282 if (!extract_from_expression(expression_tmpvar, statement.get_expression())) {
2921 return false;
2922 }
2923 282 leave_scope(statement.get_source_ref(), unwind_root);
2924 auto operation = std::make_unique<OperationReturn>(
2925 statement.get_source_ref(),
2926 expression_tmpvar
2927 282 );
2928 282 function->get_basic_block(current_block).add_operation(std::move(operation));
2929 282 }
2930 284 return true;
2931 284 }
2932
2933
2934 void
2935 318 FunctionDefinitionResolver::leave_scope(
2936 const SourceReference & src_ref,
2937 std::vector<std::string> & unwind)
2938 {
2939
2940
2/2
✓ Branch 5 taken 902 times.
✓ Branch 6 taken 318 times.
1220 for (const auto & undecl : unwind) {
2941 auto operation = std::make_unique<OperationLocalUndeclare>(
2942 src_ref,
2943 902 undecl);
2944 902 function->get_basic_block(current_block).add_operation(std::move(operation));
2945 902 }
2946 318 unwind.clear();
2947 318 }
2948
2949 bool
2950 316 FunctionDefinitionResolver::extract_from_statement_list(
2951 const StatementList & statement_list)
2952 {
2953
2954 316 bool did_return = false;
2955
2/2
✓ Branch 6 taken 1088 times.
✓ Branch 7 taken 316 times.
1404 for (const auto & statement_el : statement_list.get_statements()) {
2956 1088 const auto & statement_type = statement_el->get_statement();
2957
2/2
✓ Branch 1 taken 308 times.
✓ Branch 2 taken 780 times.
1088 if (std::holds_alternative<Gyoji::owned<StatementVariableDeclaration>>(statement_type)) {
2958 308 const auto & statement = std::get<Gyoji::owned<StatementVariableDeclaration>>(statement_type);
2959
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 308 times.
308 if (!extract_from_statement_variable_declaration(*statement)) {
2960 return false;
2961 }
2962 }
2963
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 776 times.
780 else if (std::holds_alternative<Gyoji::owned<StatementBlock>>(statement_type)) {
2964 4 const auto & statement = std::get<Gyoji::owned<StatementBlock>>(statement_type);
2965 4 scope_tracker.scope_push(statement->get_scope_body().get_source_ref());
2966
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 if (!extract_from_statement_list(statement->get_scope_body().get_statements())) {
2967 return false;
2968 }
2969 4 scope_tracker.scope_pop();
2970 }
2971
2/2
✓ Branch 1 taken 446 times.
✓ Branch 2 taken 330 times.
776 else if (std::holds_alternative<Gyoji::owned<StatementExpression>>(statement_type)) {
2972 446 const auto & statement = std::get<Gyoji::owned<StatementExpression>>(statement_type);
2973 size_t returned_tmpvar;
2974
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 446 times.
446 if (!extract_from_expression(
2975 returned_tmpvar,
2976 statement->get_expression())) {
2977 return false;
2978 }
2979 }
2980
2/2
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 296 times.
330 else if (std::holds_alternative<Gyoji::owned<StatementIfElse>>(statement_type)) {
2981 34 const auto & statement = std::get<Gyoji::owned<StatementIfElse>>(statement_type);
2982
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
34 if (!extract_from_statement_ifelse(
2983 34 *statement)) {
2984 return false;
2985 }
2986 }
2987
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 294 times.
296 else if (std::holds_alternative<Gyoji::owned<StatementWhile>>(statement_type)) {
2988 2 const auto & statement = std::get<Gyoji::owned<StatementWhile>>(statement_type);
2989
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!extract_from_statement_while(
2990 2 *statement)) {
2991 return false;
2992 }
2993 }
2994
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 292 times.
294 else if (std::holds_alternative<Gyoji::owned<StatementFor>>(statement_type)) {
2995 2 const auto & statement = std::get<Gyoji::owned<StatementFor>>(statement_type);
2996
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!extract_from_statement_for(
2997 2 *statement)) {
2998 return false;
2999 }
3000 }
3001
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 290 times.
292 else if (std::holds_alternative<Gyoji::owned<StatementLabel>>(statement_type)) {
3002 2 const auto & statement = std::get<Gyoji::owned<StatementLabel>>(statement_type);
3003
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_statement_label(*statement)) {
3004 return false;
3005 }
3006 }
3007
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 288 times.
290 else if (std::holds_alternative<Gyoji::owned<StatementGoto>>(statement_type)) {
3008 2 const auto & statement = std::get<Gyoji::owned<StatementGoto>>(statement_type);
3009
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_statement_goto(*statement)) {
3010 return false;
3011 }
3012 }
3013
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 286 times.
288 else if (std::holds_alternative<Gyoji::owned<StatementBreak>>(statement_type)) {
3014 2 const auto & statement = std::get<Gyoji::owned<StatementBreak>>(statement_type);
3015
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!extract_from_statement_break(
3016 2 *statement)) {
3017 return false;
3018 }
3019 }
3020
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 284 times.
286 else if (std::holds_alternative<Gyoji::owned<StatementContinue>>(statement_type)) {
3021 2 const auto & statement = std::get<Gyoji::owned<StatementContinue>>(statement_type);
3022
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!extract_from_statement_continue(
3023 2 *statement)) {
3024 return false;
3025 }
3026 }
3027
1/2
✓ Branch 1 taken 284 times.
✗ Branch 2 not taken.
284 else if (std::holds_alternative<Gyoji::owned<StatementReturn>>(statement_type)) {
3028 284 const auto & statement = std::get<Gyoji::owned<StatementReturn>>(statement_type);
3029 // The return may need to unwind local declarations
3030 // and ensure destructors are called.
3031
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 284 times.
284 if (!extract_from_statement_return(*statement)) {
3032 return false;
3033 }
3034 284 did_return = true;
3035 }
3036 else {
3037 fprintf(stderr, "Compiler bug, invalid statement type\n");
3038 return false;
3039 }
3040 }
3041 // If we did not do a return, we should
3042 // unwind the current scope.
3043 // If we did a return already, we will already
3044 // have called these, so we should skip it.
3045
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 284 times.
316 if (!did_return) {
3046 32 std::vector<std::string> unwind_scope = scope_tracker.get_variables_to_unwind_for_scope();
3047 32 leave_scope(statement_list.get_source_ref(), unwind_scope);
3048 32 }
3049
3050 316 return true;
3051 }
3052
3053