GCC Code Coverage Report


Directory: src/
File: src/frontend/function-lowering.cpp
Date: 2025-10-24 11:14:59
Exec Total Coverage
Lines: 915 1710 53.5%
Functions: 46 54 85.2%
Branches: 409 809 50.6%

Line Branch Exec Source
1 /* Copyright 2025 Jonathan S. Arney
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * https://github.com/jarney/gyoji/blob/master/LICENSE
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include <gyoji-frontend/function-lowering.hpp>
16 #include <gyoji-misc/jstring.hpp>
17 #include <variant>
18 #include <set>
19 #include <algorithm>
20 #include <iterator>
21 #include <stdio.h>
22
23 using namespace Gyoji::mir;
24 using namespace Gyoji::context;
25 using namespace Gyoji::frontend::tree;
26 using namespace Gyoji::frontend::lowering;
27 using namespace Gyoji::frontend::namespaces;
28
29 30 FunctionLowering::FunctionLowering(
30 Gyoji::context::CompilerContext & _compiler_context,
31 const Gyoji::frontend::ParseResult & _parse_result,
32 Gyoji::mir::MIR & _mir,
33 Gyoji::frontend::lowering::TypeLowering & _type_lowering
34 30 )
35 30 : compiler_context(_compiler_context)
36 30 , parse_result(_parse_result)
37 30 , mir(_mir)
38 30 , type_lowering(_type_lowering)
39 30 {}
40
41 30 FunctionLowering::~FunctionLowering()
42 30 {}
43
44 30 bool FunctionLowering::lower()
45 {
46 30 return extract_functions(parse_result.get_translation_unit().get_statements());
47 }
48 bool
49 12 FunctionLowering::extract_from_namespace(
50 const FileStatementNamespace & namespace_declaration)
51 {
52 12 const auto & statements = namespace_declaration.get_statement_list().get_statements();
53 12 return extract_functions(statements);
54 }
55
56 bool
57 18 FunctionLowering::extract_from_class_definition(const ClassDefinition & definition)
58 {
59 18 return true;
60 }
61
62 bool
63 42 FunctionLowering::extract_functions(const std::vector<Gyoji::owned<FileStatement>> & statements)
64 {
65
2/2
✓ Branch 5 taken 368 times.
✓ Branch 6 taken 42 times.
410 for (const auto & statement : statements) {
66 368 const auto & file_statement = statement->get_statement();
67
2/2
✓ Branch 1 taken 320 times.
✓ Branch 2 taken 48 times.
368 if (std::holds_alternative<Gyoji::owned<FileStatementFunctionDeclaration>>(file_statement)) {
68 // Nothing, no functions can exist here.
69 }
70
2/2
✓ Branch 1 taken 270 times.
✓ Branch 2 taken 50 times.
320 else if (std::holds_alternative<Gyoji::owned<FileStatementFunctionDefinition>>(file_statement)) {
71 // This is the only place that functions can be extracted from.
72 // We make this a separate object because we want convenient
73 // access to certain pieces of context used in resolution.
74 FunctionDefinitionLowering function_def_lowering(
75 compiler_context,
76 270 *std::get<Gyoji::owned<FileStatementFunctionDefinition>>(file_statement),
77 mir,
78 type_lowering
79 540 );
80
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 270 times.
270 if (!function_def_lowering.lower()) {
81 return false;
82 }
83
1/2
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
270 }
84
2/2
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 2 times.
50 else if (std::holds_alternative<Gyoji::owned<FileStatementGlobalDefinition>>(file_statement)) {
85 // Nothing, no globals can exist here.
86 // Global declarations should already be resolved by the type_lowering earlier.
87 }
88
2/2
✓ Branch 1 taken 40 times.
✓ Branch 2 taken 8 times.
48 else if (std::holds_alternative<Gyoji::owned<ClassDeclaration>>(file_statement)) {
89 // Nothing, no functions can exist here.
90 // Class declarations should already be resolved by the type_lowering earlier.
91 }
92
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 22 times.
40 else if (std::holds_alternative<Gyoji::owned<ClassDefinition>>(file_statement)) {
93 // Destructors, and methods are special cases.
94
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
18 if (!extract_from_class_definition(*std::get<Gyoji::owned<ClassDefinition>>(file_statement))) {
95 return false;
96 }
97 }
98
1/2
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
22 else if (std::holds_alternative<Gyoji::owned<EnumDefinition>>(file_statement)) {
99 // Nothing, no functions can exist here.
100 // Enums should already be resolved by the type_lowering earlier.
101 }
102
2/2
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 6 times.
22 else if (std::holds_alternative<Gyoji::owned<TypeDefinition>>(file_statement)) {
103 // Nothing, no functions can exist here.
104 // Typedefs should already be resolved by the type_lowering earlier.
105 }
106
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 4 times.
16 else if (std::holds_alternative<Gyoji::owned<FileStatementNamespace>>(file_statement)) {
107
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
12 if (!extract_from_namespace(*std::get<Gyoji::owned<FileStatementNamespace>>(file_statement))) {
108 return false;
109 }
110 }
111
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 else if (std::holds_alternative<Gyoji::owned<FileStatementUsing>>(file_statement)) {
112 // Namespace using is largely handled by the parse stage, so we don't
113 // need to do any function resolution here.
114 }
115 else {
116 compiler_context
117 .get_errors()
118 .add_simple_error(statement->get_source_ref(),
119 "Compiler bug! Please report this message(5)",
120 "Unknown statement type in variant, extracting statements from file (compiler bug)"
121 );
122 return false;
123 }
124 }
125 42 return true;
126 }
127
128 ////////////////////////////////////////////////
129 // FunctionDefinitionLowering
130 ////////////////////////////////////////////////
131
132 270 FunctionDefinitionLowering::FunctionDefinitionLowering(
133 Gyoji::context::CompilerContext & _compiler_context,
134 const Gyoji::frontend::tree::FileStatementFunctionDefinition & _function_definition,
135 Gyoji::mir::MIR & _mir,
136 Gyoji::frontend::lowering::TypeLowering & _type_lowering
137 270 )
138 270 : compiler_context(_compiler_context)
139 270 , function_definition(_function_definition)
140 270 , mir(_mir)
141 270 , type_lowering(_type_lowering)
142 270 , scope_tracker(_function_definition.get_unsafe_modifier().is_unsafe(), _compiler_context)
143 270 , class_type(nullptr)
144 270 , method(nullptr)
145 270 {}
146 270 FunctionDefinitionLowering::~FunctionDefinitionLowering()
147 270 {}
148
149 bool
150 1228 FunctionDefinitionLowering::is_method() const
151 {
152 1228 return class_type != nullptr;
153 }
154
155 bool
156 270 FunctionDefinitionLowering::lower()
157 {
158 std::string fully_qualified_function_name =
159 270 function_definition.get_name().get_fully_qualified_name();
160
161 270 bool is_unsafe = function_definition.get_unsafe_modifier().is_unsafe();
162
163 270 NS2Entity *entity = function_definition.get_name().get_ns2_entity();
164
165 // This section tries to figure out if this is a plain function
166 // or a method/destructor call on a class.
167 270 Type *maybe_class_type = mir.get_types().get_type(entity->get_parent()->get_fully_qualified_name());
168
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 264 times.
270 if (maybe_class_type != nullptr) {
169 6 const auto & method_it = maybe_class_type->get_methods().find(entity->get_name());
170
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
6 if (method_it == maybe_class_type->get_methods().end()) {
171 compiler_context
172 .get_errors()
173 .add_simple_error(
174 function_definition.get_source_ref(),
175 "Member function not declared.",
176 std::string("Member method ")
177 + entity->get_name()
178 + std::string(" was not declared in class ")
179 + entity->get_parent()->get_fully_qualified_name()
180 );
181 return false;
182 }
183 else {
184 // This is a specific member function of a class.
185 // Mark the class and method here.
186 6 class_type = maybe_class_type;
187 6 method = &method_it->second;
188 }
189 }
190
191 const Type *return_type;
192 const SourceReference *return_type_source_ref;
193
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 266 times.
270 if (function_definition.is_destructor()) {
194 8 return_type = mir.get_types().get_type("void");
195 4 return_type_source_ref = &function_definition.get_name().get_source_ref();
196 }
197 else {
198 266 const TypeSpecifier & return_type_specifier = function_definition.get_return_type();
199 266 return_type = type_lowering.extract_from_type_specifier(return_type_specifier);
200 266 return_type_source_ref = &return_type_specifier.get_source_ref();
201 }
202
203
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 270 times.
270 if (return_type == nullptr) {
204 compiler_context
205 .get_errors()
206 .add_simple_error(
207 function_definition.get_source_ref(),
208 "Return-value type not defined",
209 std::string("Return type was not declared")
210 );
211 return false;
212
213 }
214 // We should to the same check
215 // against the function declaration for a 'plain' function.
216 270 const Gyoji::mir::Symbol *symbol = mir.get_symbols().get_symbol(fully_qualified_function_name);
217
3/4
✓ Branch 0 taken 270 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 264 times.
✓ Branch 4 taken 6 times.
270 bool is_static = symbol != nullptr && symbol->get_type() == Gyoji::mir::Symbol::SYMBOL_STATIC_FUNCTION;
218
219 270 std::vector<FunctionArgument> arguments;
220 // If this is a method instead of a plain function,
221 // we add the implicit '<this>' argument as the first argument
222 // so that we can use it to get access to the class content.
223 // Note that we do not expose 'this' or 'super' as a keyword here
224 // in order to limit the damage the programmer can potentially do in
225 // leaking the 'this' pointer elsewhere, particularly in a destructor.
226
5/6
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 264 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 264 times.
270 if (is_method() && !is_static) {
227 6 class_pointer_type = mir.get_types().get_pointer_to(class_type, function_definition.get_source_ref());
228 6 std::string this_arg_name("<this>");
229 FunctionArgument arg(this_arg_name, class_pointer_type,
230 6 function_definition.get_source_ref(),
231 6 function_definition.get_source_ref());
232 6 arguments.push_back(arg);
233 6 }
234
235 270 const auto & function_argument_list = function_definition.get_arguments();
236 270 const auto & function_definition_args = function_argument_list.get_arguments();
237
238 270 bool member_conflict_errors = false;
239
240
2/2
✓ Branch 5 taken 518 times.
✓ Branch 6 taken 270 times.
788 for (const auto & function_definition_arg : function_definition_args) {
241 518 std::string name = function_definition_arg->get_identifier().get_name();
242
243 // If this is a method, we want to check that the arguments
244 // to the function don't conflict with member variable names.
245
5/6
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 512 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 512 times.
518 if (is_method() && !is_static) {
246 6 const TypeMember *member = class_type->member_get(name);
247
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (member != nullptr) {
248 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Variable Name Conflict");
249 error->add_message(
250 function_definition_arg->get_identifier().get_source_ref(),
251 std::string("Method defined argument ") + name + std::string(" which would conflict with class member name.")
252 );
253 error->add_message(
254 member->get_source_ref(),
255 std::string("Member variable declared here.")
256 );
257 compiler_context
258 .get_errors()
259 .add_error(std::move(error));
260 member_conflict_errors = true;
261 }
262 }
263
264 518 const Gyoji::mir::Type * mir_type = type_lowering.extract_from_type_specifier(function_definition_arg->get_type_specifier());
265
266 FunctionArgument arg(name, mir_type,
267 518 function_definition_arg->get_identifier().get_source_ref(),
268 1036 function_definition_arg->get_type_specifier().get_source_ref());
269 518 arguments.push_back(arg);
270
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 518 times.
518 if (!scope_tracker.add_variable(name, mir_type, function_definition_arg->get_identifier().get_source_ref())) {
271 return false;
272 }
273
2/4
✓ Branch 1 taken 518 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 518 times.
✗ Branch 5 not taken.
518 }
274
275
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 270 times.
270 if (member_conflict_errors) {
276 return false;
277 }
278
279 // Check that the arguments declared here
280 // match the function (or method) declaration,
281 // otherwise we'll end up with a badly defined
282 // function.
283
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 264 times.
270 if (is_method()) {
284 // Argument mismatch from method.
285
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
6 if (arguments.size() != method->get_arguments().size()) {
286 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Method argument mismatch");
287 error->add_message(
288 function_definition.get_source_ref(),
289 std::string("Method has ") + std::to_string(arguments.size()-1) + std::string(" arguments defined")
290 );
291 error->add_message(
292 method->get_source_ref(),
293 std::string("First declared here with ") + std::to_string(method->get_arguments().size() - (is_static ? 0 : 1) )
294 );
295 compiler_context
296 .get_errors()
297 .add_error(std::move(error));
298
299 return false;
300 }
301 6 bool arg_error = false;
302
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
6 if (method->get_return_type()->get_name() != return_type->get_name()) {
303 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Return-value does not match declaration");
304 error->add_message(
305 *return_type_source_ref,
306 std::string("Return-value defined as ") + return_type->get_name() + std::string(".")
307 );
308 error->add_message(
309 method->get_source_ref(),
310 std::string("Does not match declaration ") + method->get_return_type()->get_name()
311 );
312 compiler_context
313 .get_errors()
314 .add_error(std::move(error));
315 arg_error = true;
316 }
317
318
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 6 times.
18 for (size_t i = 0; i < arguments.size(); i++) {
319 12 const FunctionArgument & fa = arguments.at(i);
320 12 const Argument & ma = method->get_arguments().at(i);
321
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 12 times.
12 if (fa.get_type()->get_name() != ma.get_type()->get_name()) {
322 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Method argument mismatch");
323 error->add_message(
324 fa.get_type_source_ref(),
325 std::string("Argument defined as ") + fa.get_type()->get_name() + std::string(" does not match declaration.")
326 );
327 error->add_message(
328 ma.get_source_ref(),
329 std::string("First declared here as ") + ma.get_type()->get_name()
330 );
331 compiler_context
332 .get_errors()
333 .add_error(std::move(error));
334 arg_error = true;
335 }
336 }
337
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (arg_error) {
338 return false;
339 }
340 }
341 else {
342
1/2
✓ Branch 0 taken 264 times.
✗ Branch 1 not taken.
264 if (symbol == nullptr) {
343 // This is perfectly fine. It just
344 // means that there was no forward declaration
345 // for this function.
346 }
347 else {
348 // If there was a forward declaration, we need to make sure
349 // it is the correct type and matches the function signature.
350 264 const Type *symbol_type = symbol->get_mir_type();
351
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 264 times.
264 if (symbol_type->get_type() != Type::TYPE_FUNCTION_POINTER) {
352 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Symbol is not a function");
353 error->add_message(
354 function_definition.get_source_ref(),
355 std::string("Symbol ") + fully_qualified_function_name + std::string(" is not declared as a function.")
356 );
357 compiler_context
358 .get_errors()
359 .add_error(std::move(error));
360 return false;
361 }
362 264 const std::vector<Argument> & function_arguments = symbol_type->get_argument_types();
363
364
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 264 times.
264 if (arguments.size() != function_arguments.size()) {
365 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Function argument mismatch");
366 error->add_message(
367 function_definition.get_source_ref(),
368 std::string("Function has ") + std::to_string(arguments.size()) + std::string(" arguments defined")
369 );
370 error->add_message(
371 symbol_type->get_defined_source_ref(),
372 std::string("First declared here with ") + std::to_string(function_arguments.size())
373 );
374 compiler_context
375 .get_errors()
376 .add_error(std::move(error));
377
378 return false;
379 }
380 264 bool arg_error = false;
381
382
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 264 times.
264 if (symbol_type->is_unsafe() != is_unsafe) {
383 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Function safety modifier does not match declaration.");
384 error->add_message(
385 *return_type_source_ref,
386 std::string("Function defined as ") + (is_unsafe ? std::string("unsafe") : std::string("not unsafe")) + std::string(".")
387 );
388 error->add_message(
389 symbol_type->get_defined_source_ref(),
390 std::string("Does not match previous declaration as ") + (symbol_type->is_unsafe() ? std::string("unsafe") : std::string("not unsafe"))
391 );
392 compiler_context
393 .get_errors()
394 .add_error(std::move(error));
395 arg_error = true;
396 }
397
398
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 264 times.
264 if (symbol_type->get_return_type()->get_name() != return_type->get_name()) {
399 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Return-value does not match declaration");
400 error->add_message(
401 *return_type_source_ref,
402 std::string("Return-value defined as ") + return_type->get_name() + std::string(".")
403 );
404 error->add_message(
405 symbol_type->get_defined_source_ref(),
406 std::string("Does not match declaration ") + return_type->get_name()
407 );
408 compiler_context
409 .get_errors()
410 .add_error(std::move(error));
411 arg_error = true;
412 }
413
414
2/2
✓ Branch 1 taken 512 times.
✓ Branch 2 taken 264 times.
776 for (size_t i = 0; i < arguments.size(); i++) {
415 512 const FunctionArgument & fa = arguments.at(i);
416 512 const Argument & ma = function_arguments.at(i);
417
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 512 times.
512 if (fa.get_type()->get_name() != ma.get_type()->get_name()) {
418 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Method argument mismatch");
419 error->add_message(
420 fa.get_type_source_ref(),
421 std::string("Argument defined as ") + fa.get_type()->get_name() + std::string(" does not match declaration.")
422 );
423 error->add_message(
424 ma.get_source_ref(),
425 std::string("First declared here as ") + ma.get_type()->get_name()
426 );
427 compiler_context
428 .get_errors()
429 .add_error(std::move(error));
430 arg_error = true;
431 }
432 }
433
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 264 times.
264 if (arg_error) {
434 return false;
435 }
436 }
437 }
438
439 540 function = std::make_unique<Function>(
440 fully_qualified_function_name,
441 return_type,
442 arguments,
443 is_unsafe,
444 540 function_definition.get_source_ref());
445
446 // Create a new basic block for the start
447
448 270 current_block = function->add_block();
449
450
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 270 times.
270 if (!extract_from_statement_list(
451 false, // do-unwind automatically
452 270 function_definition.get_scope_body().get_statements())) {
453 return false;
454 }
455
456 //scope_tracker.dump();
457
458 // This is a list of goto operations (ScopeOperation*)
459 // each with a vector of variable declarations (std::vector<ScopeOperation*>)
460 // that represent variables that should be 'undeclared' just prior to
461 // the Jump instruction of each basic block. The first element
462 // of the pair will always be a Goto and the second will always be
463 // variable declarations. The point of this is to collect all of them
464 // here because they aren't known earlier while the funciton is still under
465 // construction so that we can insert the required instructions
466 // now that we know what should be going out of scope
467 // for the goto statements.
468 270 std::vector<std::pair<const ScopeOperation*, std::vector<const ScopeOperation*>>> goto_fixups;
469
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 270 times.
270 if (!scope_tracker.check(goto_fixups)) {
470 return false;
471 }
472
473
2/2
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 270 times.
272 for (const auto & fixup : goto_fixups) {
474 2 const ScopeOperation *goto_operation = fixup.first;
475 2 size_t basic_block_id = goto_operation->get_goto_point().get_basic_block_id();
476 2 size_t location = goto_operation->get_goto_point().get_location();
477
2/2
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 2 times.
10 for (const auto & unwind : fixup.second) {
478 // Insert the 'undeclare' operation to mark that
479 // this variable is no longer in scope.
480 8 location += undeclare_local(
481 unwind->get_variable_name(),
482 unwind->get_variable_type(),
483 basic_block_id,
484 location,
485 goto_operation->get_source_ref());
486 }
487 }
488
489 // We can now calculate the reachability graph
490 // of the basic blocks and cull any empty blocks that
491 // are unreachable. This doesn't seem very
492 // elegant, but we need to know the reachability
493 // of non-terminating blocks so we know if we need
494 // to add return statements to them
495 // or if they can just be culled.
496 //
497 // This 'reachability' graph is really implementing the
498 // 'dominating' relation on the CFG graph.
499 270 function->calculate_block_reachability();
500
501 // Case: Reachable block with no terminator.
502 // This will be a missing return, so we need to
503 // add it or raise an error.
504 // Case Reachable block with terminator.
505 // This is the normal case.
506 // Case Unreachable block with no terminator.
507 // It may still contain unreachable statements
508 // that we need to raise an error for.
509 // Case Unreachable block with terminator.
510 // It contains unreachable code, so this
511 // is an error.
512
513 // Check for missing return statements
514 // and insert them if the function is 'void'.
515 // If the function is not 'void', then
516 // we need to raise an error for it.
517 270 const auto & blocks = function->get_blocks();
518
2/2
✓ Branch 5 taken 364 times.
✓ Branch 6 taken 270 times.
634 for (const auto & block_it : blocks) {
519 364 const BasicBlock & block = *block_it.second;
520 364 if (!block.contains_terminator()
521 // A block is reachable if it is reachable from another block
522 // OR it is the 'entry' block with ID 0.
523
6/8
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 360 times.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✓ Branch 9 taken 360 times.
364 && (block.get_reachable_from().size() != 0 || block_it.first == 0)
524 ) {
525
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 if (return_type->is_void()) {
526 4 std::vector<std::string> unwind_scope = scope_tracker.get_variables_to_unwind_for_scope();
527 4 leave_scope(unwind_scope, function_definition.get_scope_body().get_source_ref());
528
529 12 function->add_operation(
530 4 block_it.first,
531 8 std::make_unique<OperationReturnVoid>(
532 *return_type_source_ref
533 )
534 );
535 4 }
536 else {
537 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Control reaches end of non-void function");
538 error->add_message(
539 function_definition.get_scope_body().get_end_source_ref(),
540 std::string("Function ")
541 + fully_qualified_function_name
542 + std::string(" returns ")
543 + return_type->get_name()
544 + std::string(" but is missing a return statement at the end of the function.")
545 );
546 error->add_message(
547 *return_type_source_ref,
548 std::string("Return type defined here")
549 );
550 compiler_context
551 .get_errors()
552 .add_error(std::move(error));
553 }
554 }
555 }
556
557 270 mir.get_functions().add_function(std::move(function));
558
559 270 return true;
560 270 }
561
562 bool
563 1254 FunctionDefinitionLowering::extract_from_expression_primary_identifier(
564 size_t & returned_tmpvar,
565 const Gyoji::frontend::tree::ExpressionPrimaryIdentifier & expression
566 )
567 {
568 // At this point, we should try to identify what this
569 // identifier actually refers to.
570 // * First look inside the function's declared variables in scope.
571 // * Next, look in the global namespace for functions.
572 // * Finally, look in the global namespace for external
573 // variables like 'global' or 'static' variables.
574 // If the identifier is a function.
575 // There are a few cases:
576 // * The it is a function and is used in an assignment
577 // to a function pointer. In this case, we should
578 // return the type as a function pointer type
579 // * If it is used in a function call directly, we should not
580 // emit the code, but should just use an 'immediate'
581 // execution of whatever it is.
582 // * Otherwise, emit it as a 'load' and just follow our nose
583 // at runtime.
584 // * Maybe we really should 'flatten' our access here.
585
586
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1254 times.
1254 if (expression.get_identifier().get_identifier_type() == Terminal::IDENTIFIER_LOCAL_SCOPE) {
587 // This block is obsolete, it should
588 // no longer be possible to get here, so we should
589 // confirm that fact and remove this block altogether.
590 compiler_context
591 .get_errors()
592 .add_simple_error(
593 expression.get_source_ref(),
594 "Local variable could not be resolved: should not be reachable.",
595 std::string("Local variable ") + expression.get_identifier().get_value() + std::string(" was not found in this scope.")
596 );
597 return false;
598 }
599
1/2
✓ Branch 2 taken 1254 times.
✗ Branch 3 not taken.
1254 else if (expression.get_identifier().get_identifier_type() == Terminal::IDENTIFIER_GLOBAL_SCOPE) {
600 // First, check to see if there is a variable of that name
601 // declared in our current scope.
602 1254 std::string local_variable_name = expression.get_identifier().get_name();
603 1254 const LocalVariable *localvar = scope_tracker.get_variable(local_variable_name);
604
2/2
✓ Branch 0 taken 1168 times.
✓ Branch 1 taken 86 times.
1254 if (localvar != nullptr) {
605 1168 returned_tmpvar = function->tmpvar_define(localvar->get_type());
606 2336 function->add_operation(
607 current_block,
608 2336 std::make_unique<OperationLocalVariable>(
609 1168 expression.get_identifier().get_source_ref(),
610 returned_tmpvar,
611 local_variable_name,
612 1168 localvar->get_type()
613 )
614 );
615
616
617 1168 return true;
618 }
619
620
2/2
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 64 times.
86 if (is_method()) {
621 22 const TypeMember *member = class_type->member_get(local_variable_name);
622 // This is a class member, so we can resolve it
623 // by dereferencing 'this'.
624
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 if (member != nullptr) {
625 4 size_t this_tmpvar = function->tmpvar_define(class_pointer_type);
626
627 8 function->add_operation(
628 current_block,
629 8 std::make_unique<OperationLocalVariable>(
630 4 expression.get_identifier().get_source_ref(),
631 this_tmpvar,
632 "<this>",
633 4 class_pointer_type
634 )
635 );
636
637 4 size_t this_reference_tmpvar = function->tmpvar_define(class_type);
638 8 function->add_operation(
639 current_block,
640 8 std::make_unique<OperationUnary>(
641 4 Operation::OP_DEREFERENCE,
642 expression.get_source_ref(),
643 this_reference_tmpvar,
644 this_tmpvar
645 )
646 );
647
648
649 4 returned_tmpvar = function->tmpvar_define(member->get_type());
650 8 function->add_operation(
651 current_block,
652 8 std::make_unique<OperationDot>(
653 expression.get_source_ref(),
654 returned_tmpvar,
655 this_reference_tmpvar,
656 local_variable_name
657 )
658 );
659 4 return true;
660 }
661 }
662
663 // Next, check for member variable and insert the 'dereference' operation
664 // and then a 'dot' operation to get the actual value of the variable.
665 // temp var for the operation should be an argument that we've defined in the
666 // scope, but only addressable by 'member' name (i.e. there is no 'this' argument
667 // according to the method).
668
669 82 std::string fully_qualified_name = expression.get_identifier().get_fully_qualified_name();
670
671 // Look in the list of functions,
672 // this might be a function pointer assignment or
673 // a global variable.
674 82 const Gyoji::mir::Symbol *symbol = mir.get_symbols().get_symbol(
675 fully_qualified_name
676 );
677 // The symbol 1) must exist AND
678 // if we're doing a method call, it must be a method
679 // AND if it's not a method call, it must be a static function.
680
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 if (symbol == nullptr) {
681 compiler_context
682 .get_errors()
683 .add_simple_error(
684 expression.get_source_ref(),
685 "Unresolved symbol",
686 std::string("Symbol ") + fully_qualified_name + std::string(" was not declared in this scope.")
687 );
688 return false;
689 }
690
691
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 82 times.
82 if (symbol->get_type() == Gyoji::mir::Symbol::SYMBOL_MEMBER_DESTRUCTOR) {
692 compiler_context
693 .get_errors()
694 .add_simple_error(
695 expression.get_source_ref(),
696 "Explicit calls to destructors are not allowed.",
697 std::string("Symbol ") + fully_qualified_name + std::string(" is a destructor and may not be called directly")
698 );
699 return false;
700 }
701 82 std::vector<size_t> partials;
702 82 returned_tmpvar = function->tmpvar_define(symbol->get_mir_type());
703 // What if a 'symbol' actually contained
704 // other objects as 'partial' operands
705 // to the function.
706 164 function->add_operation(
707 current_block,
708 164 std::make_unique<OperationSymbol>(
709 82 expression.get_identifier().get_source_ref(),
710 returned_tmpvar,
711 partials,
712 fully_qualified_name
713 )
714 );
715 82 return true;
716 1254 }
717 return false;
718 }
719
720 bool
721 4 FunctionDefinitionLowering::extract_from_expression_primary_nested(
722 size_t & returned_tmpvar,
723 const Gyoji::frontend::tree::ExpressionPrimaryNested & expression)
724 {
725 // Nested expressions don't emit blocks on their own, just run whatever is nested.
726 4 return extract_from_expression(
727 returned_tmpvar,
728 expression.get_expression()
729 4 );
730 }
731
732 bool
733 8 FunctionDefinitionLowering::extract_from_expression_primary_literal_char(
734 size_t & returned_tmpvar,
735 const Gyoji::frontend::tree::ExpressionPrimaryLiteralChar & expression)
736 {
737 8 std::string string_unescaped;
738 size_t location;
739 8 bool escape_success = Gyoji::misc::string_c_unescape(string_unescaped, location, expression.get_value(), true);
740 char c;
741
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (!escape_success) {
742 compiler_context
743 .get_errors()
744 .add_simple_error(
745 expression.get_source_ref(),
746 "Invalid Character Literal",
747 std::string("Unknown escape sequence found at character offset ") + std::to_string(location) + std::string(" in character literal")
748 );
749 c = '!';
750 }
751 else {
752
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (string_unescaped.size() != 1) {
753 compiler_context
754 .get_errors()
755 .add_simple_error(
756 expression.get_source_ref(),
757 "Invalid Character Literal",
758 std::string("Character literal must consist of a single byte.")
759 );
760 c = '%';
761 }
762 else {
763 // Finally, we're sure it's valid and
764 // has one element, we can pass it down
765 // to the operation.
766 8 c = string_unescaped[0];
767 }
768 }
769
770
771 16 returned_tmpvar = function->tmpvar_define(mir.get_types().get_type("u8"));
772 16 function->add_operation(
773 current_block,
774 16 std::make_unique<OperationLiteralChar>(
775 expression.get_source_ref(),
776 returned_tmpvar,
777 c
778 )
779 );
780
781 16 return true;
782 8 }
783
784 bool
785 12 FunctionDefinitionLowering::extract_from_expression_primary_literal_string(
786 size_t & returned_tmpvar,
787 const Gyoji::frontend::tree::ExpressionPrimaryLiteralString & expression)
788 {
789 // The hardest part here is that we need to extract the escape sequences from
790 // the source representation and place the raw data into the operation
791 // so that it is working with the actual literal value that will be
792 // placed into the machine code during the code-generation stage.
793
794 12 std::string string_unescaped;
795 size_t location;
796 12 bool escape_success = Gyoji::misc::string_c_unescape(string_unescaped, location, expression.get_value(), false);
797
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (!escape_success) {
798 compiler_context
799 .get_errors()
800 .add_simple_error(
801 expression.get_source_ref(),
802 "Invalid String Literal",
803 std::string("Unknown escape sequence found at character offset ") + std::to_string(location) + std::string(" in string")
804 );
805 // We can actually continue processing
806 // the file so we can extract more errors later.
807 // We've already found an error, so there's no danger
808 // or producing an invalid file because the code-generator
809 // won't run if there's an error.
810 }
811
812
813
814 24 returned_tmpvar = function->tmpvar_define(mir.get_types().get_type("u8*"));
815 24 function->add_operation(
816 current_block,
817 24 std::make_unique<OperationLiteralString>(
818 expression.get_source_ref(),
819 returned_tmpvar,
820 string_unescaped
821 )
822 );
823 24 return true;
824 12 }
825
826 bool
827 2 FunctionDefinitionLowering::create_constant_integer_one(
828 const Gyoji::mir::Type *type,
829 size_t & returned_tmpvar,
830 const Gyoji::context::SourceReference & _src_ref
831 )
832 {
833 Gyoji::frontend::integers::ParseLiteralIntResult parse_result;
834 2 parse_result.parsed_type = type;
835
836
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()) {
837 case Type::TYPE_PRIMITIVE_u8:
838 parse_result.u8_value = (unsigned char)1;
839 break;
840 case Type::TYPE_PRIMITIVE_u16:
841 parse_result.u16_value = (unsigned short)1;
842 break;
843 2 case Type::TYPE_PRIMITIVE_u32:
844 2 parse_result.u32_value = (unsigned int)1;
845 2 break;
846 case Type::TYPE_PRIMITIVE_u64:
847 parse_result.u64_value = (unsigned long)1;
848 break;
849 case Type::TYPE_PRIMITIVE_i8:
850 parse_result.i8_value = (char)1;
851 break;
852 case Type::TYPE_PRIMITIVE_i16:
853 parse_result.i16_value = (short)1;
854 break;
855 case Type::TYPE_PRIMITIVE_i32:
856 parse_result.i32_value = (int)1;
857 break;
858 case Type::TYPE_PRIMITIVE_i64:
859 parse_result.i64_value = (long)1;
860 break;
861 default:
862 compiler_context
863 .get_errors()
864 .add_simple_error(
865 _src_ref,
866 "Compiler Bug! Invalid integer literal",
867 std::string("Unsupported primitive literal type creating literal one value") + type->get_name()
868 );
869 return false;
870 }
871 2 return create_constant_integer(parse_result, returned_tmpvar, _src_ref);
872 }
873
874 bool
875 244 FunctionDefinitionLowering::create_constant_integer(
876 const Gyoji::frontend::integers::ParseLiteralIntResult & parse_result,
877 size_t & returned_tmpvar,
878 const Gyoji::context::SourceReference & _src_ref
879 )
880 {
881 244 const Type *type_part = parse_result.parsed_type;
882 244 returned_tmpvar = function->tmpvar_define(type_part);
883
884 244 Gyoji::owned<Gyoji::mir::Operation> operation;
885
8/9
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 166 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.
244 switch (type_part->get_type()) {
886 12 case Type::TYPE_PRIMITIVE_u8:
887 {
888 12 operation = std::make_unique<OperationLiteralInt>(
889 _src_ref,
890 returned_tmpvar,
891 24 type_part->get_type(),
892 12 parse_result.u8_value
893 12 );
894 }
895 26 case Type::TYPE_PRIMITIVE_u16:
896 {
897 26 operation = std::make_unique<OperationLiteralInt>(
898 _src_ref,
899 returned_tmpvar,
900 26 type_part->get_type(),
901 26 parse_result.u16_value
902 26 );
903 }
904 26 break;
905 166 case Type::TYPE_PRIMITIVE_u32:
906 {
907 166 operation = std::make_unique<OperationLiteralInt>(
908 _src_ref,
909 returned_tmpvar,
910 166 type_part->get_type(),
911 166 parse_result.u32_value
912 166 );
913 }
914 166 break;
915 18 case Type::TYPE_PRIMITIVE_u64:
916 {
917 18 operation = std::make_unique<OperationLiteralInt>(
918 _src_ref,
919 returned_tmpvar,
920 18 type_part->get_type(),
921 18 parse_result.u64_value
922 18 );
923 }
924 18 break;
925
926 // Signed
927 10 case Type::TYPE_PRIMITIVE_i8:
928 {
929 10 operation = std::make_unique<OperationLiteralInt>(
930 _src_ref,
931 returned_tmpvar,
932 20 type_part->get_type(),
933 10 parse_result.i8_value
934 10 );
935 }
936 16 case Type::TYPE_PRIMITIVE_i16:
937 {
938 16 operation = std::make_unique<OperationLiteralInt>(
939 _src_ref,
940 returned_tmpvar,
941 16 type_part->get_type(),
942 16 parse_result.i16_value
943 16 );
944 }
945 16 break;
946 8 case Type::TYPE_PRIMITIVE_i32:
947 {
948 8 operation = std::make_unique<OperationLiteralInt>(
949 _src_ref,
950 returned_tmpvar,
951 8 type_part->get_type(),
952 8 parse_result.i32_value
953 8 );
954 }
955 8 break;
956 10 case Type::TYPE_PRIMITIVE_i64:
957 {
958 10 operation = std::make_unique<OperationLiteralInt>(
959 _src_ref,
960 returned_tmpvar,
961 10 type_part->get_type(),
962 10 parse_result.i64_value
963 10 );
964 }
965 10 break;
966
967 default:
968 compiler_context
969 .get_errors()
970 .add_simple_error(
971 _src_ref,
972 "Compiler Bug! Invalid integer literal",
973 std::string("Unsupported primitive literal type ") + type_part->get_name()
974 );
975 return false;
976 }
977 244 function->add_operation(current_block, std::move(operation));
978
979 244 return true;
980 244 }
981
982 // There is a LOT of logic here for handling various
983 // kinds of integer literals that might be specified here.
984 // Of course, the simplest is something like
985 // u32 a = 10;
986 // We also support more interesting
987 // things like hexidecimal, octal, and binary
988 // such at:
989 // u64 a = 0xfeeda747u64;
990 // u16 b = 0o03423;
991 // We also allow separators for readability, consider:
992 // u64 h = 0xfeed_a_747_u64;
993 // u16 o = 0o034_23;
994 // u16 b = 0b0100_0200_u16;
995 // Combine that with range-checking all of the
996 // possible representations, and you have a lot of things
997 // that can go wrong such as a number being badly formatted
998 // or with characters outside the radix.
999 // Condier what happens when the user specifies these:
1000 // u8 n = 342343u8; // Number too big for u8
1001 // u8 n = -12u8; // It's an unsigned, but you put a negative there.
1002 // u8 n = 0b23334u8; // It's a binary number, but it's not really binary (and too big also).
1003 // SO many error messages so that the user knows PRECISELY what they did wrong and where.
1004 bool
1005 242 FunctionDefinitionLowering::extract_from_expression_primary_literal_int(
1006 size_t & returned_tmpvar,
1007 const Gyoji::frontend::tree::ExpressionPrimaryLiteralInt & expression)
1008 {
1009 Gyoji::frontend::integers::ParseLiteralIntResult parse_result;
1010 242 const Terminal & literal_int_token = expression.get_literal_int_token();
1011 242 bool parsed = parse_literal_int(compiler_context, mir.get_types(), literal_int_token, parse_result);
1012
2/4
✓ Branch 0 taken 242 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 242 times.
242 if (!parsed || parse_result.parsed_type == nullptr) {
1013 return false;
1014 }
1015
1016 242 return create_constant_integer(
1017 parse_result,
1018 returned_tmpvar,
1019 242 literal_int_token.get_source_ref());
1020 }
1021
1022 bool
1023 6 FunctionDefinitionLowering::extract_from_expression_primary_literal_float(
1024 size_t & returned_tmpvar,
1025 const Gyoji::frontend::tree::ExpressionPrimaryLiteralFloat & expression)
1026 {
1027 6 std::string literal_type_name = expression.get_type();
1028 6 returned_tmpvar = function->tmpvar_define(mir.get_types().get_type(literal_type_name));
1029 6 Gyoji::owned<OperationLiteralFloat> operation;
1030 char *endptr;
1031 6 const char *source_cstring = expression.get_value().c_str();
1032 6 size_t length = expression.get_value().size();
1033 6 errno = 0;
1034
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
6 if (literal_type_name == "f32") {
1035
1036 2 float converted_value = strtof(source_cstring, &endptr);
1037
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (endptr != (source_cstring + length)) {
1038 compiler_context
1039 .get_errors()
1040 .add_simple_error(
1041 expression.get_source_ref(),
1042 "Invalid floating-point literal",
1043 std::string("Could not correctly parse the literal value.")
1044 );
1045 return false;
1046 }
1047
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (errno == ERANGE) {
1048 compiler_context
1049 .get_errors()
1050 .add_simple_error(
1051 expression.get_source_ref(),
1052 "Invalid floating-point literal",
1053 std::string("Floating-point literal does not fit in the range of an f32.")
1054 );
1055 return false;
1056 }
1057 4 operation = std::make_unique<OperationLiteralFloat>(
1058 expression.get_source_ref(),
1059 returned_tmpvar,
1060 4 (float)converted_value
1061 2 );
1062 }
1063 else {
1064 4 double converted_value = strtod(source_cstring, &endptr);
1065
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (endptr != (source_cstring + length)) {
1066 compiler_context
1067 .get_errors()
1068 .add_simple_error(
1069 expression.get_source_ref(),
1070 "Invalid floating-point literal",
1071 std::string("Could not correctly parse the literal value.")
1072 );
1073 return false;
1074 }
1075
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (errno == ERANGE) {
1076 compiler_context
1077 .get_errors()
1078 .add_simple_error(
1079 expression.get_source_ref(),
1080 "Invalid floating-point literal",
1081 std::string("Floating-point literal does not fit in the range of an f64.")
1082 );
1083 return false;
1084 }
1085 8 operation = std::make_unique<OperationLiteralFloat>(
1086 expression.get_source_ref(),
1087 returned_tmpvar,
1088 converted_value
1089 4 );
1090 }
1091 6 function->add_operation(current_block, std::move(operation));
1092 6 return true;
1093 6 }
1094
1095 bool
1096 FunctionDefinitionLowering::extract_from_expression_postfix_array_index(
1097 size_t & returned_tmpvar,
1098 const ExpressionPostfixArrayIndex & expression)
1099 {
1100 size_t array_tmpvar;
1101 size_t index_tmpvar;
1102 if (!extract_from_expression(array_tmpvar, expression.get_array())) {
1103 return false;
1104 }
1105 if (!extract_from_expression(index_tmpvar, expression.get_index())) {
1106 return false;
1107 }
1108
1109 const Type *array_type = function->tmpvar_get(array_tmpvar);
1110 if (!array_type->is_array()) {
1111 compiler_context
1112 .get_errors()
1113 .add_simple_error(
1114 expression.get_array().get_source_ref(),
1115 "Array type must be an array type",
1116 std::string("Type of array is not an array type.")
1117 );
1118 return false;
1119 }
1120
1121 const Type *index_type = function->tmpvar_get(index_tmpvar);
1122 if (index_type->get_type() != Type::TYPE_PRIMITIVE_u32) {
1123 compiler_context
1124 .get_errors()
1125 .add_simple_error(
1126 expression.get_index().get_source_ref(),
1127 "Array index must be an unsigned 32-bit (u32) type",
1128 std::string("Type of index is not a u32 index")
1129 );
1130 return false;
1131 }
1132
1133 returned_tmpvar = function->tmpvar_define(array_type->get_pointer_target());
1134 function->add_operation(
1135 current_block,
1136 std::make_unique<OperationArrayIndex>(
1137 expression.get_source_ref(),
1138 returned_tmpvar,
1139 array_tmpvar,
1140 index_tmpvar
1141 )
1142 );
1143
1144 return true;
1145 }
1146
1147
1148 bool
1149 84 FunctionDefinitionLowering::check_function_call_signature(
1150 bool is_method,
1151 const std::vector<size_t> & passed_arguments,
1152 const std::vector<const Gyoji::context::SourceReference *> & passed_src_refs,
1153 const Type *function_pointer_type,
1154 const Gyoji::context::SourceReference & src_ref
1155 )
1156 {
1157 // Here, we need to check that the arguments we're passing to the
1158 // function match the function/method signature.
1159 84 const std::vector<Argument> & function_pointer_args = function_pointer_type->get_argument_types();
1160
1161 84 bool is_ok = true;
1162
1163
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 84 times.
84 if (passed_arguments.size() != function_pointer_args.size()) {
1164 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>(
1165 (is_method ?
1166 std::string("Wrong number of arguments passed to method call.") :
1167 std::string("Wrong number of arguments passed to function call.")
1168 )
1169 );
1170 error->add_message(src_ref,
1171 std::string("Passing ")
1172 + std::to_string(passed_arguments.size() - (is_method ? 1 : 0))
1173 + ( is_method ? std::string(" to method") : std::string(" to function."))
1174 );
1175 error->add_message(function_pointer_type->get_declared_source_ref(),
1176 (is_method ? std::string("Method was declared to have ") : std::string("Function was declared to have "))
1177 + std::to_string(function_pointer_args.size() - (is_method ? 1 : 0))
1178 + std::string(" arguments.")
1179 );
1180 compiler_context
1181 .get_errors()
1182 .add_error(std::move(error));
1183 is_ok = false;
1184 }
1185
1186 // If we're not in an unsafe context (i.e. we're in safe mode)
1187 // then we cannot call an unsafe function.
1188
2/2
✓ Branch 1 taken 82 times.
✓ Branch 2 taken 2 times.
84 if (!scope_tracker.is_unsafe()) {
1189
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 82 times.
82 if (function_pointer_type->is_unsafe()) {
1190 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>(
1191 (is_method ?
1192 std::string("Calling an unsafe method from a safe context.") :
1193 std::string("Calling an unsafe function from a safe context.")
1194 )
1195 );
1196 error->add_message(src_ref,
1197 (is_method ? std::string("Method ") : std::string("Function ") )
1198 + std::string("is declared as unsafe, but this is not inside a scope marked unsafe.")
1199 );
1200 compiler_context
1201 .get_errors()
1202 .add_error(std::move(error));
1203 is_ok = false;
1204 }
1205 }
1206
1207 84 size_t minsize = std::min(passed_arguments.size(), function_pointer_args.size());
1208
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 84 times.
176 for (size_t i = 0; i < minsize; i++) {
1209 92 const Type *passed_type = function->tmpvar_get(passed_arguments.at(i));
1210 92 const SourceReference & passed_src_ref = *passed_src_refs.at(i);
1211 92 const Argument & arg = function_pointer_args.at(i);
1212 92 const Type *required_type = arg.get_type();
1213
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 92 times.
92 if (required_type->get_name() != passed_type->get_name()) {
1214 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Incorrect argument type passed to call");
1215 error->add_message(passed_src_ref,
1216 std::string("Passing type ")
1217 + passed_type->get_name()
1218 + std::string(" as argument ")
1219 + std::to_string(i+1)
1220
1221 );
1222 error->add_message(arg.get_source_ref(),
1223 std::string("Argument type was declared as ")
1224 + required_type->get_name()
1225 );
1226 compiler_context
1227 .get_errors()
1228 .add_error(std::move(error));
1229 is_ok = false;
1230 }
1231 }
1232
1233 84 return is_ok;
1234 }
1235
1236 bool
1237 84 FunctionDefinitionLowering::extract_from_expression_postfix_function_call(
1238 size_t & returned_tmpvar,
1239 const ExpressionPostfixFunctionCall & expression)
1240 {
1241 // Extract the expression itself from the arguments.
1242 size_t function_type_tmpvar;
1243
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 84 times.
84 if (!extract_from_expression(function_type_tmpvar, expression.get_function())) {
1244 return false;
1245 }
1246 // For method calls, function_type_tmpvar must somehow encode
1247 // both the type of function pointer to call
1248 // as well as another tmpvar for the class value itself.
1249 // The value will end up encoding pair of "class_tmpvar" and "function_type_tmpvar".
1250
1251 84 std::vector<size_t> passed_arguments;
1252 84 std::vector<const Gyoji::context::SourceReference *> passed_src_refs;
1253
2/2
✓ Branch 6 taken 88 times.
✓ Branch 7 taken 84 times.
172 for (const auto & arg_expr : expression.get_arguments().get_arguments()) {
1254 size_t arg_returned_value;
1255
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 88 times.
88 if (!extract_from_expression(arg_returned_value, *arg_expr)) {
1256 return false;
1257 }
1258 88 passed_arguments.push_back(arg_returned_value);
1259 88 passed_src_refs.push_back(&arg_expr->get_source_ref());
1260 }
1261
1262 84 const Type *call_type = function->tmpvar_get(function_type_tmpvar);
1263
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
84 if (call_type->get_type() != Type::TYPE_FUNCTION_POINTER) {
1264 compiler_context
1265 .get_errors()
1266 .add_simple_error(
1267 expression.get_function().get_source_ref(),
1268 "Called object is not a function.",
1269 std::string("Type of object being called is not a function, but is a ") + call_type->get_name() + std::string(" instead.")
1270 );
1271 return false;
1272 }
1273
1274 84 const Type *function_pointer_type = call_type;
1275 // We declare that we return the vale that the function
1276 // will return.
1277
1278 // Pull out any of the 'partial' operations.
1279 84 const Operation* function_operation = function->tmpvar_get_operation(function_type_tmpvar);
1280
1281
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
84 if (function_operation != nullptr) {
1282
2/2
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 84 times.
86 for (const size_t & operand : function_operation->get_operands()) {
1283 2 passed_arguments.insert(passed_arguments.begin(), operand);
1284 2 passed_src_refs.insert(passed_src_refs.begin(), &expression.get_source_ref());
1285 }
1286 }
1287
1288 84 returned_tmpvar = function->tmpvar_define(function_pointer_type->get_return_type());
1289
1290
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 66 times.
84 if (is_method()) {
1291
2/2
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 16 times.
18 if (passed_arguments.size() == function_pointer_type->get_argument_types().size()-1) {
1292 2 size_t this_tmpvar = function->tmpvar_define(class_type);
1293 4 function->add_operation(
1294 current_block,
1295 4 std::make_unique<OperationLocalVariable>(
1296 expression.get_source_ref(),
1297 this_tmpvar,
1298 "<this>",
1299 2 class_pointer_type
1300 )
1301 );
1302
1303 2 const Type *this_pointer_type = mir.get_types().get_pointer_to(class_type, expression.get_source_ref());
1304 2 size_t this_pointer_tmpvar = function->tmpvar_define(this_pointer_type);
1305 4 function->add_operation(
1306 current_block,
1307 4 std::make_unique<OperationUnary>(
1308 2 Operation::OP_ADDRESSOF,
1309 expression.get_source_ref(),
1310 this_pointer_tmpvar,
1311 this_tmpvar
1312 )
1313 );
1314
1315 2 passed_arguments.insert(passed_arguments.begin(), this_pointer_tmpvar);
1316 2 passed_src_refs.insert(passed_src_refs.begin(), &expression.get_source_ref());
1317 }
1318 }
1319
1320 // Check that the function signature we're calling matches
1321 // the declaration of that function.
1322
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 84 times.
84 if (!check_function_call_signature(false, passed_arguments, passed_src_refs, function_pointer_type, expression.get_source_ref())) {
1323 return false;
1324 }
1325
1326 168 function->add_operation(
1327 current_block,
1328 168 std::make_unique<OperationFunctionCall>(
1329 84 Operation::OP_FUNCTION_CALL,
1330 expression.get_source_ref(),
1331 returned_tmpvar,
1332 function_type_tmpvar,
1333 passed_arguments
1334 )
1335 );
1336
1337
1338 84 return true;
1339 84 }
1340
1341 bool
1342 2 FunctionDefinitionLowering::extract_from_expression_postfix_dot(
1343 size_t & returned_tmpvar,
1344 const ExpressionPostfixDot & expression)
1345 {
1346 size_t class_tmpvar;
1347
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_expression(class_tmpvar, expression.get_expression())) {
1348 return false;
1349 }
1350
1351 2 const Type *class_type = function->tmpvar_get(class_tmpvar);
1352
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (class_type->get_type() == Type::TYPE_REFERENCE) {
1353 const Type * target = class_type->get_pointer_target();
1354 size_t class_reference_tmpvar = function->tmpvar_define(target);
1355 function->add_operation(
1356 current_block,
1357 std::make_unique<OperationUnary>(
1358 Operation::OP_DEREFERENCE,
1359 expression.get_source_ref(),
1360 class_reference_tmpvar,
1361 class_tmpvar
1362 )
1363 );
1364 class_type = target;
1365 class_tmpvar = class_reference_tmpvar;
1366 }
1367
1368
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (class_type->get_type() != Type::TYPE_COMPOSITE) {
1369 compiler_context
1370 .get_errors()
1371 .add_simple_error(
1372 expression.get_expression().get_source_ref(),
1373 "Member access must be applied to a class.",
1374 std::string("Type of object being accessed is not a class, but is a ") + class_type->get_name() + std::string(" instead.")
1375 );
1376 return false;
1377 }
1378 2 const std::string & member_name = expression.get_identifier().get_name();
1379
1380 2 const TypeMember *member = class_type->member_get(member_name);
1381
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (member != nullptr) {
1382 returned_tmpvar = function->tmpvar_define(member->get_type());
1383 function->add_operation(
1384 current_block,
1385 std::make_unique<OperationDot>(
1386 expression.get_source_ref(),
1387 returned_tmpvar,
1388 class_tmpvar,
1389 member_name
1390 )
1391 );
1392 return true;
1393 }
1394
1395 2 const TypeMethod *method = class_type->method_get(member_name);
1396
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (method != nullptr) {
1397 2 std::string fully_qualified_function_name = class_type->get_name() + NS2Context::NAMESPACE_DELIMITER + member_name;
1398 2 const Gyoji::mir::Symbol *symbol = mir.get_symbols().get_symbol(fully_qualified_function_name);
1399
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
2 if (symbol == nullptr || symbol->get_type() != Gyoji::mir::Symbol::SYMBOL_MEMBER_METHOD) {
1400 compiler_context
1401 .get_errors()
1402 .add_simple_error(
1403 expression.get_expression().get_source_ref(),
1404 "Class method not found.",
1405 std::string("Method ") + fully_qualified_function_name + std::string(" was not found on class ") + class_type->get_name()
1406 );
1407 return false;
1408 }
1409
1410 2 const Type * class_pointer_type = mir.get_types().get_pointer_to(class_type, expression.get_source_ref());
1411
1412 2 size_t class_pointer_tmpvar = function->tmpvar_define(class_pointer_type);
1413 4 function->add_operation(
1414 current_block,
1415 4 std::make_unique<OperationUnary>(
1416 2 Operation::OP_ADDRESSOF,
1417 expression.get_source_ref(),
1418 class_pointer_tmpvar,
1419 class_tmpvar
1420 )
1421 );
1422
1423 2 std::vector<size_t> partial_operands;
1424 2 partial_operands.push_back(class_pointer_tmpvar);
1425
1426 2 returned_tmpvar = function->tmpvar_define(symbol->get_mir_type());
1427 4 function->add_operation(
1428 current_block,
1429 4 std::make_unique<OperationSymbol>(
1430 2 expression.get_expression().get_source_ref(),
1431 returned_tmpvar,
1432 partial_operands,
1433 fully_qualified_function_name
1434 )
1435 );
1436 2 return true;
1437 2 }
1438 compiler_context
1439 .get_errors()
1440 .add_simple_error(
1441 expression.get_expression().get_source_ref(),
1442 "Member or method not found.",
1443 std::string("Class does not have member or method '") + member_name + std::string("'.")
1444 );
1445
1446 return false;
1447 2 }
1448
1449 bool
1450 FunctionDefinitionLowering::extract_from_expression_postfix_arrow(
1451 size_t & returned_tmpvar,
1452 const ExpressionPostfixArrow & expression)
1453 {
1454 size_t classptr_tmpvar;
1455 if (!extract_from_expression(classptr_tmpvar, expression.get_expression())) {
1456 return false;
1457 }
1458
1459 const Type *classptr_type = function->tmpvar_get(classptr_tmpvar);
1460 if (classptr_type->get_type() != Type::TYPE_POINTER) {
1461 compiler_context
1462 .get_errors()
1463 .add_simple_error(
1464 expression.get_expression().get_source_ref(),
1465 "Arrow (->) operator must be used on a pointer to a class.",
1466 std::string("Type of object being accessed is not a pointer to a class, but is a ") + classptr_type->get_name() + std::string(" instead.")
1467 );
1468 return false;
1469 }
1470 const Type *class_type = classptr_type->get_pointer_target();
1471
1472 if (class_type->get_type() != Type::TYPE_COMPOSITE) {
1473 compiler_context
1474 .get_errors()
1475 .add_simple_error(
1476 expression.get_expression().get_source_ref(),
1477 "Arrow (->) access must be applied to a pointer to a class.",
1478 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.")
1479 );
1480 return false;
1481 }
1482
1483 if (!scope_tracker.is_unsafe()) {
1484 compiler_context
1485 .get_errors()
1486 .add_simple_error(
1487 expression.get_expression().get_source_ref(),
1488 "De-referencing pointers (->) must be done inside an 'unsafe' block.",
1489 std::string("De-referencing a pointer outside an 'unsafe' block breaks the safety guarantees of the language.")
1490 );
1491 return false;
1492 }
1493 // First takes 'operand' as a pointer
1494 // and de-references it into an lvalue.
1495 size_t class_reference_tmpvar = function->tmpvar_define(class_type);
1496 function->add_operation(
1497 current_block,
1498 std::make_unique<OperationUnary>(
1499 Operation::OP_DEREFERENCE,
1500 expression.get_source_ref(),
1501 class_reference_tmpvar,
1502 classptr_tmpvar
1503 )
1504 );
1505
1506
1507 const std::string & member_name = expression.get_identifier().get_name();
1508 const TypeMember *member = class_type->member_get(member_name);
1509 if (member == nullptr) {
1510 compiler_context
1511 .get_errors()
1512 .add_simple_error(
1513 expression.get_expression().get_source_ref(),
1514 "Attempt to access an undeclared member",
1515 std::string("Member ") + member_name + std::string(" was not declared in ") + class_type->get_name()
1516 );
1517 return false;
1518 }
1519
1520 returned_tmpvar = function->tmpvar_define(member->get_type());
1521 function->add_operation(
1522 current_block,
1523 std::make_unique<OperationDot>(
1524 expression.get_source_ref(),
1525 returned_tmpvar,
1526 class_reference_tmpvar,
1527 member_name
1528 )
1529 );
1530 return true;
1531
1532 }
1533
1534 bool
1535 2 FunctionDefinitionLowering::extract_from_expression_postfix_incdec(
1536 size_t & returned_tmpvar,
1537 const ExpressionPostfixIncDec & expression)
1538 {
1539 size_t operand_tmpvar;
1540
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_expression(operand_tmpvar, expression.get_expression())) {
1541 return false;
1542 }
1543
1544 2 return create_incdec_operation(
1545 expression.get_source_ref(),
1546 returned_tmpvar,
1547 operand_tmpvar,
1548 2 (expression.get_type() == ExpressionPostfixIncDec::INCREMENT), // is_increment
1549 true // is_postfix
1550 2 );
1551 }
1552
1553 bool
1554 2 FunctionDefinitionLowering::create_incdec_operation(
1555 const Gyoji::context::SourceReference & src_ref,
1556 size_t & returned_tmpvar,
1557 const size_t & operand_tmpvar,
1558 bool is_increment,
1559 bool is_postfix
1560 )
1561 {
1562 // This should be implemented as:
1563 // _1 = load(variable)
1564 // _2 = constant(1)
1565 // _3 = add/sub _1 _2 <==== Add or subtract depending on is_increment
1566 // store(_3, variable)
1567 // _4 = <==== This will be either _1 or _3 depending on is_postfix.
1568 //
1569
1570 2 const Type *operand_type = function->tmpvar_get(operand_tmpvar);
1571 size_t constant_one_tmpvar;
1572
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!create_constant_integer_one(
1573 operand_type,
1574 constant_one_tmpvar,
1575 src_ref
1576 )) {
1577 return false;
1578 }
1579
1580
1581 2 size_t addresult_tmpvar = function->tmpvar_duplicate(operand_tmpvar);
1582
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (is_increment) {
1583 4 function->add_operation(
1584 current_block,
1585 2 std::make_unique<OperationBinary>(
1586 4 Operation::OP_ADD,
1587 src_ref,
1588 addresult_tmpvar,
1589 operand_tmpvar,
1590 constant_one_tmpvar
1591 )
1592 );
1593 }
1594 else {
1595 function->add_operation(
1596 current_block,
1597 std::make_unique<OperationBinary>(
1598 Operation::OP_SUBTRACT,
1599 src_ref,
1600 addresult_tmpvar,
1601 operand_tmpvar,
1602 constant_one_tmpvar
1603 )
1604 );
1605 }
1606
1607 // We perform a 'store' to store
1608 // the value back into the variable.
1609 2 size_t ignore_tmpvar = function->tmpvar_duplicate(operand_tmpvar);
1610 4 function->add_operation(
1611 current_block,
1612 2 std::make_unique<OperationBinary>(
1613 2 Operation::OP_ASSIGN,
1614 src_ref,
1615 ignore_tmpvar,
1616 operand_tmpvar,
1617 addresult_tmpvar
1618 )
1619 );
1620
1621 // This is a post-decrement, so we return
1622 // the value as it was before we incremented
1623 // it.
1624
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 returned_tmpvar = is_postfix ? operand_tmpvar : addresult_tmpvar;
1625 2 return true;
1626 }
1627
1628 bool
1629 16 FunctionDefinitionLowering::extract_from_expression_unary_prefix(
1630 size_t & returned_tmpvar,
1631 const ExpressionUnaryPrefix & expression)
1632 {
1633
1634 // Extract the prior expression
1635 // and if not otherwise specified,
1636 // the operation will return the
1637 // same type as the operand.
1638 size_t operand_tmpvar;
1639
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
16 if (!extract_from_expression(
1640 operand_tmpvar,
1641 expression.get_expression()
1642 )) {
1643 return false;
1644 }
1645
1646 16 const Type *operand_type = function->tmpvar_get(operand_tmpvar);
1647
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (operand_type == nullptr) {
1648 compiler_context
1649 .get_errors()
1650 .add_simple_error(
1651 expression.get_source_ref(),
1652 "Compiler bug! Please report this message(4)",
1653 "Operand must be a valid type."
1654 );
1655 }
1656
1657 16 ExpressionUnaryPrefix::OperationType op_type = expression.get_type();
1658
3/9
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
16 switch (op_type) {
1659 case ExpressionUnaryPrefix::INCREMENT:
1660 {
1661 return create_incdec_operation(
1662 expression.get_source_ref(),
1663 returned_tmpvar,
1664 operand_tmpvar,
1665 true, // is_increment
1666 false // is_postfix
1667 );
1668 }
1669 break;
1670 case ExpressionUnaryPrefix::DECREMENT:
1671 {
1672 return create_incdec_operation(
1673 expression.get_source_ref(),
1674 returned_tmpvar,
1675 operand_tmpvar,
1676 false, // is_increment
1677 false // is_postfix
1678 );
1679 }
1680 break;
1681 8 case ExpressionUnaryPrefix::ADDRESSOF:
1682 {
1683 8 const Type * pointer_to_operand_type = mir.get_types().get_pointer_to(operand_type, expression.get_source_ref());
1684 8 returned_tmpvar = function->tmpvar_define(pointer_to_operand_type);
1685 16 function->add_operation(
1686 current_block,
1687 16 std::make_unique<OperationUnary>(
1688 8 Operation::OP_ADDRESSOF,
1689 expression.get_source_ref(),
1690 returned_tmpvar,
1691 operand_tmpvar
1692 )
1693 );
1694 }
1695 8 break;
1696 6 case ExpressionUnaryPrefix::DEREFERENCE:
1697 {
1698 6 bool is_ok = true;
1699
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
6 if (!operand_type->is_pointer() && !operand_type->is_reference()) {
1700 compiler_context
1701 .get_errors()
1702 .add_simple_error(
1703 expression.get_expression().get_source_ref(),
1704 "Cannot dereference non-pointer",
1705 std::string("Attempting to de-reference non-pointer type ") + operand_type->get_name()
1706 );
1707 is_ok = false;
1708 }
1709
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
6 if (!scope_tracker.is_unsafe() && !operand_type->is_reference()) {
1710 compiler_context
1711 .get_errors()
1712 .add_simple_error(
1713 expression.get_expression().get_source_ref(),
1714 "De-referencing pointers (*) must be done inside an 'unsafe' block.",
1715 std::string("De-referencing a pointer outside an 'unsafe' block breaks the safety guarantees of the language.")
1716 );
1717 is_ok = false;
1718 }
1719
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!is_ok) {
1720 return false;
1721 }
1722 6 returned_tmpvar = function->tmpvar_define(operand_type->get_pointer_target());
1723 12 function->add_operation(
1724 current_block,
1725 12 std::make_unique<OperationUnary>(
1726 6 Operation::OP_DEREFERENCE,
1727 expression.get_source_ref(),
1728 returned_tmpvar,
1729 operand_tmpvar
1730 )
1731 );
1732 }
1733 6 break;
1734 case ExpressionUnaryPrefix::PLUS:
1735 {
1736 // Unary plus does nothing, really, so why bother? We just don't
1737 // bother to do anything and just wire the return into the operand
1738 // directly instead of creating a new tmpvar and assigning it.
1739 returned_tmpvar = operand_tmpvar;
1740 }
1741 break;
1742 case ExpressionUnaryPrefix::MINUS:
1743 {
1744 returned_tmpvar = function->tmpvar_duplicate(operand_tmpvar);
1745 function->add_operation(
1746 current_block,
1747 std::make_unique<OperationUnary>(
1748 Operation::OP_NEGATE,
1749 expression.get_source_ref(),
1750 returned_tmpvar,
1751 operand_tmpvar
1752 )
1753 );
1754 }
1755 break;
1756 case ExpressionUnaryPrefix::BITWISE_NOT:
1757 {
1758 returned_tmpvar = function->tmpvar_duplicate(operand_tmpvar);
1759 function->add_operation(
1760 current_block,
1761 std::make_unique<OperationUnary>(
1762 Operation::OP_BITWISE_NOT,
1763 expression.get_source_ref(),
1764 returned_tmpvar,
1765 operand_tmpvar
1766 )
1767 );
1768 }
1769 break;
1770 2 case ExpressionUnaryPrefix::LOGICAL_NOT:
1771 {
1772
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if (!function->tmpvar_get(operand_tmpvar)->is_bool()) {
1773 compiler_context
1774 .get_errors()
1775 .add_simple_error(
1776 expression.get_expression().get_source_ref(),
1777 "Logical not (!) must operate on 'bool' expressions.",
1778 std::string("Type of condition expression should be 'bool' and was ") + function->tmpvar_get(operand_tmpvar)->get_name()
1779 );
1780 }
1781 2 returned_tmpvar = function->tmpvar_duplicate(operand_tmpvar);
1782 4 function->add_operation(
1783 current_block,
1784 4 std::make_unique<OperationUnary>(
1785 2 Operation::OP_LOGICAL_NOT,
1786 expression.get_source_ref(),
1787 returned_tmpvar,
1788 operand_tmpvar
1789 )
1790 );
1791 }
1792 2 break;
1793 default:
1794 compiler_context
1795 .get_errors()
1796 .add_simple_error(
1797 expression.get_expression().get_source_ref(),
1798 "Compiler Bug!",
1799 "Encountered unknown unary prefix expression"
1800 );
1801 return false;
1802 }
1803 16 return true;
1804 }
1805 bool
1806 8 FunctionDefinitionLowering::extract_from_expression_unary_sizeof_type(
1807 size_t & returned_tmpvar,
1808 const ExpressionUnarySizeofType & expression)
1809 {
1810 8 const Type * operand_type = type_lowering.extract_from_type_specifier(expression.get_type_specifier());
1811 16 const Type * u64_type = mir.get_types().get_type("u64");
1812 8 returned_tmpvar = function->tmpvar_define(u64_type);
1813 16 function->add_operation(
1814 current_block,
1815 16 std::make_unique<OperationSizeofType>(
1816 expression.get_source_ref(),
1817 returned_tmpvar,
1818 operand_type
1819 )
1820 );
1821 8 return true;
1822 }
1823
1824 bool
1825 136 FunctionDefinitionLowering::numeric_widen(
1826 const Gyoji::context::SourceReference & _src_ref,
1827 size_t & _widen_var,
1828 const Type *widen_to
1829 )
1830 {
1831 // The new b is the result of widening b
1832 // to the type of a.
1833 Operation::OperationType widen_type;
1834
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()) {
1835 60 widen_type = Operation::OP_WIDEN_SIGNED;
1836 }
1837
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()) {
1838 60 widen_type = Operation::OP_WIDEN_UNSIGNED;
1839 }
1840
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 else if (widen_to->is_float()) {
1841 16 widen_type = Operation::OP_WIDEN_FLOAT;
1842 }
1843 else {
1844 fprintf(stderr, "Compiler Bug! Widening number of unknown type\n");
1845 return false;
1846 }
1847
1848 136 size_t widened_var = function->tmpvar_define(widen_to);
1849 272 function->add_operation(
1850 current_block,
1851 272 std::make_unique<OperationCast>(
1852 widen_type,
1853 _src_ref,
1854 widened_var,
1855 _widen_var,
1856 widen_to
1857 )
1858 );
1859 136 _widen_var = widened_var;
1860 136 return true;
1861 }
1862 bool
1863 242 FunctionDefinitionLowering::numeric_widen_binary_operation(
1864 const Gyoji::context::SourceReference & _src_ref,
1865 size_t & a_tmpvar,
1866 size_t & b_tmpvar,
1867 const Gyoji::mir::Type *atype,
1868 const Gyoji::mir::Type *btype,
1869 const Gyoji::mir::Type **widened
1870 )
1871 {
1872
2/2
✓ Branch 1 taken 210 times.
✓ Branch 2 taken 32 times.
242 if (atype->is_integer()) {
1873 // Deal with signed-ness. We can't mix signed and unsigned.
1874
5/6
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 110 times.
✓ Branch 4 taken 100 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 100 times.
✓ Branch 7 taken 110 times.
210 if (atype->is_signed() && btype->is_signed()) {
1875
2/2
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 70 times.
100 if (atype->get_primitive_size() > btype->get_primitive_size()) {
1876
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 if (!numeric_widen(_src_ref, b_tmpvar, atype)) {
1877 return false;
1878 }
1879 30 *widened = atype;
1880 }
1881
2/2
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 40 times.
70 else if (atype->get_primitive_size() < btype->get_primitive_size()) {
1882
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 if (!numeric_widen(_src_ref, a_tmpvar, btype)) {
1883 return false;
1884 }
1885 30 *widened = btype;
1886 }
1887 else {
1888 // Both types are the same size already, so we do nothing
1889 // and it doesn't matter whether we picked 'a' or 'b'.
1890 40 *widened = atype;
1891 }
1892 }
1893
3/6
✓ Branch 1 taken 110 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 110 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 110 times.
✗ Branch 7 not taken.
110 else if (atype->is_unsigned() && btype->is_unsigned()) {
1894
2/2
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 80 times.
110 if (atype->get_primitive_size() > btype->get_primitive_size()) {
1895
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 if (!numeric_widen(_src_ref, b_tmpvar, atype)) {
1896 return false;
1897 }
1898 30 *widened = atype;
1899 }
1900
2/2
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 50 times.
80 else if (atype->get_primitive_size() < btype->get_primitive_size()) {
1901
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 if (!numeric_widen(_src_ref, a_tmpvar, btype)) {
1902 return false;
1903 }
1904 30 *widened = btype;
1905 }
1906 else {
1907 // Both types are the same size already, so we do nothing
1908 // and it doesn't matter whether we picked 'a' or 'b'.
1909 50 *widened = atype;
1910 }
1911 }
1912 else {
1913 compiler_context
1914 .get_errors()
1915 .add_simple_error(
1916 _src_ref,
1917 "Type mismatch in binary operation",
1918 std::string("The type of operands both signed or unsigned. Automatic cast from signed to unsigned is not supported. a= ") +
1919 atype->get_name() + std::string(" b=") + btype->get_name()
1920 );
1921 return false;
1922 }
1923 }
1924 // We don't have to check because we already
1925 // know that we have a float in the else.
1926 else {
1927
2/2
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 24 times.
32 if (atype->get_primitive_size() > btype->get_primitive_size()) {
1928
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (!numeric_widen(_src_ref, b_tmpvar, atype)) {
1929 return false;
1930 }
1931 8 *widened = atype;
1932 }
1933
2/2
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 16 times.
24 else if (atype->get_primitive_size() < btype->get_primitive_size()) {
1934
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (!numeric_widen(_src_ref, a_tmpvar, btype)) {
1935 return false;
1936 }
1937 8 *widened = btype;
1938 }
1939 else {
1940 16 *widened = atype;
1941 }
1942 }
1943 242 return true;
1944 }
1945
1946 bool
1947 242 FunctionDefinitionLowering::handle_binary_operation_arithmetic(
1948 const Gyoji::context::SourceReference & _src_ref,
1949 Operation::OperationType type,
1950 size_t & returned_tmpvar,
1951 size_t a_tmpvar,
1952 size_t b_tmpvar
1953 )
1954 {
1955 242 const Type *atype = function->tmpvar_get(a_tmpvar);
1956 242 const Type *btype = function->tmpvar_get(b_tmpvar);
1957 // Check that both operands are numeric.
1958
3/6
✓ Branch 1 taken 242 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 242 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 242 times.
242 if (!atype->is_numeric() || !btype->is_numeric()) {
1959 compiler_context
1960 .get_errors()
1961 .add_simple_error(
1962 _src_ref,
1963 "Type mismatch in binary operation",
1964 std::string("The type of operands should be numeric, but were: a=") + atype->get_name() + std::string(" b=") + btype->get_name()
1965 );
1966 return false;
1967 }
1968 // Special-case for modulo because it doesn't support floating-point types.
1969
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 200 times.
242 if (type == Operation::OP_MODULO) {
1970
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()) {
1971 compiler_context
1972 .get_errors()
1973 .add_simple_error(
1974 _src_ref,
1975 "Type mismatch in binary operation",
1976 std::string("The type of operands should be integer, but were a=") + atype->get_name() + std::string(" b=") + btype->get_name()
1977 );
1978 return false;
1979 }
1980 }
1981
1982 // The arguments must be either both integer
1983 // or both float.
1984
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 242 times.
242 if (!(
1985
3/4
✓ Branch 1 taken 210 times.
✓ Branch 2 taken 32 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 210 times.
242 (atype->is_integer() && btype->is_integer()) ||
1986
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())
1987 )) {
1988 compiler_context
1989 .get_errors()
1990 .add_simple_error(
1991 _src_ref,
1992 "Type mismatch in binary operation",
1993 std::string("The type of operands both integer or floating-point types. Automatic cast from int to float is not supported. a=") +
1994 atype->get_name() + std::string(" b=") + btype->get_name()
1995 );
1996 return false;
1997 }
1998
1999 // Now, both of them are either int or float, so we need to handle each separately
2000 // so that we can choose the appropriate cast type.
2001 242 const Type *widened = nullptr;
2002
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 242 times.
242 if (!numeric_widen_binary_operation(_src_ref, a_tmpvar, b_tmpvar, atype, btype, &widened)) {
2003 return false;
2004 }
2005
2006 // The return type is whatever
2007 // we widened the add to be.
2008 242 returned_tmpvar = function->tmpvar_define(widened);
2009
2010 // We emit the appropriate operation as either an integer or
2011 // floating-point add depending on which one it was.
2012 // The back-end may assume that the operand types are both
2013 // integer or floating point of the same type and
2014 // the return type will also be of the same type.
2015 484 function->add_operation(
2016 current_block,
2017 484 std::make_unique<OperationBinary>(
2018 type,
2019 _src_ref,
2020 returned_tmpvar,
2021 a_tmpvar,
2022 b_tmpvar
2023 )
2024 );
2025
2026 242 return true;
2027 }
2028
2029 bool
2030 FunctionDefinitionLowering::handle_binary_operation_logical(
2031 const Gyoji::context::SourceReference & _src_ref,
2032 Operation::OperationType type,
2033 size_t & returned_tmpvar,
2034 size_t a_tmpvar,
2035 size_t b_tmpvar
2036 )
2037 {
2038 const Type *atype = function->tmpvar_get(a_tmpvar);
2039 const Type *btype = function->tmpvar_get(b_tmpvar);
2040 // Check that both operands are numeric.
2041 if (!atype->is_bool() || !btype->is_bool()) {
2042 compiler_context
2043 .get_errors()
2044 .add_simple_error(
2045 _src_ref,
2046 "Type mismatch in logical operation",
2047 std::string("The type of operands should be bool , but were: a=") + atype->get_name() + std::string(" b=") + btype->get_name()
2048 );
2049 return false;
2050 }
2051 function->add_operation(
2052 current_block,
2053 std::make_unique<OperationBinary>(
2054 type,
2055 _src_ref,
2056 returned_tmpvar,
2057 a_tmpvar,
2058 b_tmpvar
2059 )
2060 );
2061 return true;
2062 }
2063
2064 bool
2065 FunctionDefinitionLowering::handle_binary_operation_bitwise(
2066 const Gyoji::context::SourceReference & _src_ref,
2067 Operation::OperationType type,
2068 size_t & returned_tmpvar,
2069 size_t a_tmpvar,
2070 size_t b_tmpvar
2071 )
2072 {
2073 const Type *atype = function->tmpvar_get(a_tmpvar);
2074 const Type *btype = function->tmpvar_get(b_tmpvar);
2075 // Check that both operands are numeric.
2076 if (!atype->is_unsigned() || !btype->is_unsigned()) {
2077 compiler_context
2078 .get_errors()
2079 .add_simple_error(
2080 _src_ref,
2081 "Type mismatch in binary operation",
2082 std::string("The type of operands should be unsigned integers, but were: a=") + atype->get_name() + std::string(" b=") + btype->get_name()
2083 );
2084 return false;
2085 }
2086 // Now widen them to the appropriate sizes.
2087 const Type *widened = nullptr;
2088 if (!numeric_widen_binary_operation(_src_ref, a_tmpvar, b_tmpvar, atype, btype, &widened)) {
2089 return false;
2090 }
2091
2092 // The return type is whatever
2093 // we widened the add to be.
2094 returned_tmpvar = function->tmpvar_define(widened);
2095
2096 // We emit the appropriate operation as either an integer or
2097 // floating-point add depending on which one it was.
2098 // The back-end may assume that the operand types are both
2099 // integer or floating point of the same type and
2100 // the return type will also be of the same type.
2101 function->add_operation(
2102 current_block,
2103 std::make_unique<OperationBinary>(
2104 type,
2105 _src_ref,
2106 returned_tmpvar,
2107 a_tmpvar,
2108 b_tmpvar
2109 )
2110 );
2111
2112 return true;
2113 }
2114
2115
2116 bool
2117 FunctionDefinitionLowering::handle_binary_operation_shift(
2118 const Gyoji::context::SourceReference & _src_ref,
2119 Operation::OperationType type,
2120 size_t & returned_tmpvar,
2121 size_t a_tmpvar,
2122 size_t b_tmpvar
2123 )
2124 {
2125 const Type *atype = function->tmpvar_get(a_tmpvar);
2126 const Type *btype = function->tmpvar_get(b_tmpvar);
2127 // Check that both operands are numeric.
2128 if (!atype->is_unsigned() || !btype->is_unsigned()) {
2129 compiler_context
2130 .get_errors()
2131 .add_simple_error(
2132 _src_ref,
2133 "Type mismatch in binary operation",
2134 std::string("The type of operands should be unsigned integers, but were: a=") + atype->get_name() + std::string(" b=") + btype->get_name()
2135 );
2136 return false;
2137 }
2138
2139 // Notably, we never widen a shift operation.
2140 // Instead, we operate on whatever it is because
2141 // it will end up being masked down to an 8-bit
2142 // value to avoid overflowing the LHS when the
2143 // shift happens if it's any bigger.
2144
2145 // We always return the same size that the LHS is.
2146 returned_tmpvar = function->tmpvar_define(atype);
2147
2148 // We emit the appropriate operation as either an integer or
2149 // floating-point add depending on which one it was.
2150 // The back-end may assume that the operand types are both
2151 // integer or floating point of the same type and
2152 // the return type will also be of the same type.
2153 function->add_operation(
2154 current_block,
2155 std::make_unique<OperationBinary>(
2156 type,
2157 _src_ref,
2158 returned_tmpvar,
2159 a_tmpvar,
2160 b_tmpvar
2161 )
2162 );
2163
2164 return true;
2165 }
2166
2167 bool
2168 40 FunctionDefinitionLowering::handle_binary_operation_compare(
2169 const Gyoji::context::SourceReference & _src_ref,
2170 Operation::OperationType type,
2171 size_t & returned_tmpvar,
2172 size_t a_tmpvar,
2173 size_t b_tmpvar
2174 )
2175 {
2176 40 const Type *atype = function->tmpvar_get(a_tmpvar);
2177 40 const Type *btype = function->tmpvar_get(b_tmpvar);
2178 // Check that both operands are the same type.
2179
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 40 times.
40 if (atype->get_name() != btype->get_name()) {
2180 compiler_context
2181 .get_errors()
2182 .add_simple_error(
2183 _src_ref,
2184 "Type mismatch in compare operation",
2185 std::string("The operands of a comparision should be the same type, but were: a=") + atype->get_name() + std::string(" b=") + btype->get_name()
2186 );
2187 return false;
2188 }
2189
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 40 times.
40 if (atype->is_void()) {
2190 compiler_context
2191 .get_errors()
2192 .add_simple_error(
2193 _src_ref,
2194 "Type mismatch in compare operation",
2195 std::string("The operands of a comparison must not be void, but were: a=") + atype->get_name() + std::string(" b=") + btype->get_name()
2196 );
2197 return false;
2198 }
2199
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 40 times.
40 if (atype->is_composite()) {
2200 compiler_context
2201 .get_errors()
2202 .add_simple_error(
2203 _src_ref,
2204 "Type mismatch in compare operation",
2205 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()
2206 );
2207 return false;
2208 }
2209 40 if (
2210
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 40 times.
80 (atype->is_pointer() || atype->is_reference())
2211
2/4
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 40 times.
80 &&
2212 !(type == Operation::OP_COMPARE_EQUAL ||
2213 type == Operation::OP_COMPARE_NOT_EQUAL)
2214 ) {
2215 compiler_context
2216 .get_errors()
2217 .add_simple_error(
2218 _src_ref,
2219 "Type mismatch in compare operation",
2220 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()
2221 );
2222 return false;
2223 }
2224
2225
2226 // Notably, we never widen a shift operation.
2227 // Instead, we operate on whatever it is because
2228 // it will end up being masked down to an 8-bit
2229 // value to avoid overflowing the LHS when the
2230 // shift happens if it's any bigger.
2231
2232 // We always return the same size that the LHS is.
2233 80 returned_tmpvar = function->tmpvar_define(mir.get_types().get_type("bool"));
2234
2235 // We emit the appropriate operation as either an integer or
2236 // floating-point add depending on which one it was.
2237 // The back-end may assume that the operand types are both
2238 // integer or floating point of the same type and
2239 // the return type will also be of the same type.
2240 80 function->add_operation(
2241 current_block,
2242 80 std::make_unique<OperationBinary>(
2243 type,
2244 _src_ref,
2245 returned_tmpvar,
2246 a_tmpvar,
2247 b_tmpvar
2248 )
2249 );
2250
2251 40 return true;
2252 }
2253
2254 bool
2255 386 FunctionDefinitionLowering::handle_binary_operation_assignment(
2256 const Gyoji::context::SourceReference & _src_ref,
2257 Operation::OperationType type,
2258 size_t & returned_tmpvar,
2259 size_t a_tmpvar,
2260 size_t b_tmpvar
2261 )
2262 {
2263 386 const Type *atype = function->tmpvar_get(a_tmpvar);
2264 386 const Type *btype = function->tmpvar_get(b_tmpvar);
2265 // Check that both operands are the same type.
2266
2/2
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 380 times.
386 if (atype->get_name() != btype->get_name()) {
2267 // If we're assigning a reference to a pointer, we
2268 // should allow it in some circumstances.
2269
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
6 if (atype->is_reference() && btype->is_pointer()) {
2270 if (!scope_tracker.is_unsafe()) {
2271 compiler_context
2272 .get_errors()
2273 .add_simple_error(
2274 _src_ref,
2275 "Assigning a reference to a raw pointer must be done inside an 'unsafe' block",
2276 std::string("Assigning a pointer to a reference must be done inside an unsafe block")
2277 );
2278 return false;
2279 }
2280 }
2281 // If we're assigning a reference to anything else,
2282 // we're 'borrowing' from that thing.
2283 // Only thing is, we have to do the 'addressof' here.
2284
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 else if (atype->is_reference()) {
2285 const Type *variable_pointer_type = mir.get_types().get_pointer_to(btype, _src_ref);
2286 size_t variable_pointer_tmpvar = function->tmpvar_define(variable_pointer_type);
2287 function->add_operation(
2288 current_block,
2289 std::make_unique<OperationUnary>(
2290 Operation::OP_ADDRESSOF,
2291 _src_ref,
2292 variable_pointer_tmpvar,
2293 b_tmpvar
2294 )
2295 );
2296 b_tmpvar = variable_pointer_tmpvar;
2297 }
2298
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
6 else if (atype->is_pointer() && btype->is_reference()) {
2299 // Nothing to do. This is a valid assignment
2300 // even inside an unsafe block.
2301 }
2302 // Should we model anonymous structures as types for the purposes of assignment?
2303
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
6 else if (atype->is_composite() && btype->is_anonymous()) {
2304
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
6 if (btype->get_members().size() != btype->get_members().size()) {
2305 fprintf(stderr, "Anonymous assignment error\n");
2306 compiler_context
2307 .get_errors()
2308 .add_simple_error(
2309 _src_ref,
2310 "Type mismatch in assignment operation, wrong number of fields.",
2311 std::string("The operands of an assignment should be the same type, but were: a=") + atype->get_name() + std::string(" b=") + btype->get_name()
2312 );
2313 return false;
2314 }
2315 // It's ok to proceed with an assignment, let the
2316 // codegen layer deal with making the copy.
2317 }
2318 else {
2319 fprintf(stderr, "Class to class assignment error %s %s.\n", atype->get_name().c_str(), btype->get_name().c_str());
2320 compiler_context
2321 .get_errors()
2322 .add_simple_error(
2323 _src_ref,
2324 "Type mismatch in assignment operation",
2325 std::string("The operands of an assignment should be the same type, but were: a=") + atype->get_name() + std::string(" b=") + btype->get_name()
2326 );
2327 return false;
2328 }
2329 }
2330
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 386 times.
386 if (atype->is_void()) {
2331 compiler_context
2332 .get_errors()
2333 .add_simple_error(
2334 _src_ref,
2335 "Type mismatch in assignment operation",
2336 std::string("The operands of an assignment must not be void, but were: a=") + atype->get_name() + std::string(" b=") + btype->get_name()
2337 );
2338 return false;
2339 }
2340 // Notably, we never widen a shift operation.
2341 // Instead, we operate on whatever it is because
2342 // it will end up being masked down to an 8-bit
2343 // value to avoid overflowing the LHS when the
2344 // shift happens if it's any bigger.
2345
2346 // We always return the same size that the LHS is.
2347 386 returned_tmpvar = function->tmpvar_define(atype);
2348
2349 // We emit the appropriate operation as either an integer or
2350 // floating-point add depending on which one it was.
2351 // The back-end may assume that the operand types are both
2352 // integer or floating point of the same type and
2353 // the return type will also be of the same type.
2354 772 function->add_operation(
2355 current_block,
2356 772 std::make_unique<OperationBinary>(
2357 type,
2358 _src_ref,
2359 returned_tmpvar,
2360 // a_tmpvar is an lvalue and b_tmpvar is an rvalue
2361 // should we wait to resolve the rvalue
2362 // until we actually consume it?
2363 a_tmpvar,
2364 b_tmpvar
2365 )
2366 );
2367
2368 386 return true;
2369 }
2370
2371 bool
2372 646 FunctionDefinitionLowering::extract_from_expression_binary(
2373 size_t & returned_tmpvar,
2374 const ExpressionBinary & expression)
2375 {
2376 size_t a_tmpvar;
2377 size_t b_tmpvar;
2378
2379
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 646 times.
646 if (!extract_from_expression(a_tmpvar, expression.get_a())) {
2380 return false;
2381 }
2382
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 646 times.
646 if (!extract_from_expression(b_tmpvar, expression.get_b())) {
2383 return false;
2384 }
2385
2386 ExpressionBinary::OperationType op_type;
2387 646 op_type = expression.get_operator();
2388
2389
2/2
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 594 times.
646 if (op_type == ExpressionBinary::ADD) {
2390
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 52 times.
52 if (!handle_binary_operation_arithmetic(
2391 expression.get_source_ref(),
2392 Operation::OP_ADD,
2393 returned_tmpvar,
2394 a_tmpvar,
2395 b_tmpvar)) {
2396 return false;
2397 }
2398 }
2399
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 546 times.
594 else if (op_type == ExpressionBinary::SUBTRACT) {
2400
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
48 if (!handle_binary_operation_arithmetic(
2401 expression.get_source_ref(),
2402 Operation::OP_SUBTRACT,
2403 returned_tmpvar,
2404 a_tmpvar,
2405 b_tmpvar)) {
2406 return false;
2407 }
2408 }
2409
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 498 times.
546 else if (op_type == ExpressionBinary::MULTIPLY) {
2410
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
48 if (!handle_binary_operation_arithmetic(
2411 expression.get_source_ref(),
2412 Operation::OP_MULTIPLY,
2413 returned_tmpvar,
2414 a_tmpvar,
2415 b_tmpvar)) {
2416 return false;
2417 }
2418 }
2419
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 450 times.
498 else if (op_type == ExpressionBinary::DIVIDE) {
2420
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
48 if (!handle_binary_operation_arithmetic(
2421 expression.get_source_ref(),
2422 Operation::OP_DIVIDE,
2423 returned_tmpvar,
2424 a_tmpvar,
2425 b_tmpvar)) {
2426 return false;
2427 }
2428 }
2429
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 408 times.
450 else if (op_type == ExpressionBinary::MODULO) {
2430
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 42 times.
42 if (!handle_binary_operation_arithmetic(
2431 expression.get_source_ref(),
2432 Operation::OP_MODULO,
2433 returned_tmpvar,
2434 a_tmpvar,
2435 b_tmpvar)) {
2436 return false;
2437 }
2438 }
2439
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 408 times.
408 else if (op_type == ExpressionBinary::LOGICAL_AND) {
2440 if (!handle_binary_operation_logical(
2441 expression.get_source_ref(),
2442 Operation::OP_LOGICAL_AND,
2443 returned_tmpvar,
2444 a_tmpvar,
2445 b_tmpvar)) {
2446 return false;
2447 }
2448 }
2449
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 408 times.
408 else if (op_type == ExpressionBinary::LOGICAL_OR) {
2450 if (!handle_binary_operation_logical(
2451 expression.get_source_ref(),
2452 Operation::OP_LOGICAL_OR,
2453 returned_tmpvar,
2454 a_tmpvar,
2455 b_tmpvar)) {
2456 return false;
2457 }
2458 }
2459
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 408 times.
408 else if (op_type == ExpressionBinary::BITWISE_AND) {
2460 if (!handle_binary_operation_bitwise(
2461 expression.get_source_ref(),
2462 Operation::OP_BITWISE_AND,
2463 returned_tmpvar,
2464 a_tmpvar,
2465 b_tmpvar)) {
2466 return false;
2467 }
2468 }
2469
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 408 times.
408 else if (op_type == ExpressionBinary::BITWISE_OR) {
2470 if (!handle_binary_operation_bitwise(
2471 expression.get_source_ref(),
2472 Operation::OP_BITWISE_OR,
2473 returned_tmpvar,
2474 a_tmpvar,
2475 b_tmpvar
2476 )) {
2477 return false;
2478 }
2479 }
2480
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 408 times.
408 else if (op_type == ExpressionBinary::BITWISE_XOR) {
2481 if (!handle_binary_operation_bitwise(
2482 expression.get_source_ref(),
2483 Operation::OP_BITWISE_XOR,
2484 returned_tmpvar,
2485 a_tmpvar,
2486 b_tmpvar
2487 )) {
2488 return false;
2489 }
2490 }
2491
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 408 times.
408 else if (op_type == ExpressionBinary::SHIFT_LEFT) {
2492 if (!handle_binary_operation_shift(
2493 expression.get_source_ref(),
2494 Operation::OP_SHIFT_LEFT,
2495 returned_tmpvar,
2496 a_tmpvar,
2497 b_tmpvar
2498 )) {
2499 return false;
2500 }
2501 }
2502
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 408 times.
408 else if (op_type == ExpressionBinary::SHIFT_RIGHT) {
2503 if (!handle_binary_operation_shift(
2504 expression.get_source_ref(),
2505 Operation::OP_SHIFT_RIGHT,
2506 returned_tmpvar,
2507 a_tmpvar,
2508 b_tmpvar
2509 )) {
2510 return false;
2511 }
2512 }
2513
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 402 times.
408 else if (op_type == ExpressionBinary::COMPARE_LESS) {
2514
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if (!handle_binary_operation_compare(
2515 expression.get_source_ref(),
2516 Operation::OP_COMPARE_LESS,
2517 returned_tmpvar,
2518 a_tmpvar,
2519 b_tmpvar
2520 )) {
2521 return false;
2522 }
2523 }
2524
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 400 times.
402 else if (op_type == ExpressionBinary::COMPARE_GREATER) {
2525
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!handle_binary_operation_compare(
2526 expression.get_source_ref(),
2527 Operation::OP_COMPARE_GREATER,
2528 returned_tmpvar,
2529 a_tmpvar,
2530 b_tmpvar
2531 )) {
2532 return false;
2533 }
2534 }
2535
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 400 times.
400 else if (op_type == ExpressionBinary::COMPARE_LESS_EQUAL) {
2536 if (!handle_binary_operation_compare(
2537 expression.get_source_ref(),
2538 Operation::OP_COMPARE_LESS_EQUAL,
2539 returned_tmpvar,
2540 a_tmpvar,
2541 b_tmpvar
2542 )) {
2543 return false;
2544 }
2545 }
2546
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 400 times.
400 else if (op_type == ExpressionBinary::COMPARE_GREATER_EQUAL) {
2547 if (!handle_binary_operation_compare(
2548 expression.get_source_ref(),
2549 Operation::OP_COMPARE_GREATER_EQUAL,
2550 returned_tmpvar,
2551 a_tmpvar,
2552 b_tmpvar
2553 )) {
2554 return false;
2555 }
2556 }
2557
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 386 times.
400 else if (op_type == ExpressionBinary::COMPARE_EQUAL) {
2558
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
14 if (!handle_binary_operation_compare(
2559 expression.get_source_ref(),
2560 Operation::OP_COMPARE_EQUAL,
2561 returned_tmpvar,
2562 a_tmpvar,
2563 b_tmpvar
2564 )) {
2565 return false;
2566 }
2567 }
2568
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 368 times.
386 else if (op_type == ExpressionBinary::COMPARE_NOT_EQUAL) {
2569
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
18 if (!handle_binary_operation_compare(
2570 expression.get_source_ref(),
2571 Operation::OP_COMPARE_NOT_EQUAL,
2572 returned_tmpvar,
2573 a_tmpvar,
2574 b_tmpvar
2575 )) {
2576 return false;
2577 }
2578 }
2579
2/2
✓ Branch 0 taken 364 times.
✓ Branch 1 taken 4 times.
368 else if (op_type == ExpressionBinary::ASSIGNMENT) {
2580
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 364 times.
364 if (!handle_binary_operation_assignment(
2581 expression.get_source_ref(),
2582 Operation::OP_ASSIGN,
2583 returned_tmpvar,
2584 a_tmpvar,
2585 b_tmpvar
2586 )) {
2587 return false;
2588 }
2589 }
2590
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 else if (op_type == ExpressionBinary::MUL_ASSIGNMENT) {
2591 // This is just syntax sugar for * followed by =
2592 size_t arithmetic_tmpvar;
2593 if (!handle_binary_operation_arithmetic(
2594 expression.get_source_ref(),
2595 Operation::OP_MULTIPLY,
2596 arithmetic_tmpvar,
2597 a_tmpvar,
2598 b_tmpvar
2599 )) {
2600 return false;
2601 }
2602 if (!handle_binary_operation_assignment(
2603 expression.get_source_ref(),
2604 Operation::OP_ASSIGN,
2605 returned_tmpvar,
2606 a_tmpvar,
2607 arithmetic_tmpvar
2608 )) {
2609 return false;
2610 }
2611 }
2612
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 else if (op_type == ExpressionBinary::DIV_ASSIGNMENT) {
2613 // This is just syntax sugar for / followed by =
2614 size_t arithmetic_tmpvar;
2615 if (!handle_binary_operation_arithmetic(
2616 expression.get_source_ref(),
2617 Operation::OP_DIVIDE,
2618 arithmetic_tmpvar,
2619 a_tmpvar,
2620 b_tmpvar
2621 )) {
2622 return false;
2623 }
2624 if (!handle_binary_operation_assignment(
2625 expression.get_source_ref(),
2626 Operation::OP_ASSIGN,
2627 returned_tmpvar,
2628 a_tmpvar,
2629 arithmetic_tmpvar
2630 )) {
2631 return false;
2632 }
2633 }
2634
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 else if (op_type == ExpressionBinary::MOD_ASSIGNMENT) {
2635 // This is just syntax sugar for % followed by =
2636 size_t arithmetic_tmpvar;
2637 if (!handle_binary_operation_arithmetic(
2638 expression.get_source_ref(),
2639 Operation::OP_MODULO,
2640 arithmetic_tmpvar,
2641 a_tmpvar,
2642 b_tmpvar
2643 )) {
2644 return false;
2645 }
2646 if (!handle_binary_operation_assignment(
2647 expression.get_source_ref(),
2648 Operation::OP_ASSIGN,
2649 returned_tmpvar,
2650 a_tmpvar,
2651 arithmetic_tmpvar
2652 )) {
2653 return false;
2654 }
2655 }
2656
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 else if (op_type == ExpressionBinary::ADD_ASSIGNMENT) {
2657 // This is just syntax sugar for + followed by =
2658 size_t arithmetic_tmpvar;
2659
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!handle_binary_operation_arithmetic(
2660 expression.get_source_ref(),
2661 Operation::OP_ADD,
2662 arithmetic_tmpvar,
2663 a_tmpvar,
2664 b_tmpvar
2665 )) {
2666 return false;
2667 }
2668
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!handle_binary_operation_assignment(
2669 expression.get_source_ref(),
2670 Operation::OP_ASSIGN,
2671 returned_tmpvar,
2672 a_tmpvar,
2673 arithmetic_tmpvar
2674 )) {
2675 return false;
2676 }
2677 }
2678
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 else if (op_type == ExpressionBinary::SUB_ASSIGNMENT) {
2679 // This is just syntax sugar for - followed by =
2680 size_t arithmetic_tmpvar;
2681
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!handle_binary_operation_arithmetic(
2682 expression.get_source_ref(),
2683 Operation::OP_SUBTRACT,
2684 arithmetic_tmpvar,
2685 a_tmpvar,
2686 b_tmpvar
2687 )) {
2688 return false;
2689 }
2690
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!handle_binary_operation_assignment(
2691 expression.get_source_ref(),
2692 Operation::OP_ASSIGN,
2693 returned_tmpvar,
2694 a_tmpvar,
2695 arithmetic_tmpvar
2696 )) {
2697 return false;
2698 }
2699 }
2700 else if (op_type == ExpressionBinary::LEFT_ASSIGNMENT) {
2701 // This is just syntax sugar for << followed by =
2702 size_t arithmetic_tmpvar;
2703 if (!handle_binary_operation_shift(
2704 expression.get_source_ref(),
2705 Operation::OP_SHIFT_LEFT,
2706 arithmetic_tmpvar,
2707 a_tmpvar,
2708 b_tmpvar
2709 )) {
2710 return false;
2711 }
2712 if (!handle_binary_operation_assignment(
2713 expression.get_source_ref(),
2714 Operation::OP_ASSIGN,
2715 returned_tmpvar,
2716 a_tmpvar,
2717 arithmetic_tmpvar
2718 )) {
2719 return false;
2720 }
2721 }
2722 else if (op_type == ExpressionBinary::RIGHT_ASSIGNMENT) {
2723 // This is just syntax sugar for >> followed by =
2724 size_t arithmetic_tmpvar;
2725 if (!handle_binary_operation_shift(
2726 expression.get_source_ref(),
2727 Operation::OP_SHIFT_RIGHT,
2728 arithmetic_tmpvar,
2729 a_tmpvar,
2730 b_tmpvar
2731 )) {
2732 return false;
2733 }
2734 if (!handle_binary_operation_assignment(
2735 expression.get_source_ref(),
2736 Operation::OP_ASSIGN,
2737 returned_tmpvar,
2738 a_tmpvar,
2739 arithmetic_tmpvar
2740 )) {
2741 return false;
2742 }
2743 }
2744 else if (op_type == ExpressionBinary::AND_ASSIGNMENT) {
2745 // This is just syntax sugar for & followed by =
2746 size_t arithmetic_tmpvar;
2747 if (!handle_binary_operation_bitwise(
2748 expression.get_source_ref(),
2749 Operation::OP_BITWISE_AND,
2750 arithmetic_tmpvar,
2751 a_tmpvar,
2752 b_tmpvar
2753 )) {
2754 return false;
2755 }
2756 if (!handle_binary_operation_assignment(
2757 expression.get_source_ref(),
2758 Operation::OP_ASSIGN,
2759 returned_tmpvar,
2760 a_tmpvar,
2761 arithmetic_tmpvar
2762 )) {
2763 return false;
2764 }
2765 }
2766 else if (op_type == ExpressionBinary::OR_ASSIGNMENT) {
2767 // This is just syntax sugar for | followed by =
2768 size_t arithmetic_tmpvar;
2769 if (!handle_binary_operation_bitwise(
2770 expression.get_source_ref(),
2771 Operation::OP_BITWISE_OR,
2772 arithmetic_tmpvar,
2773 a_tmpvar,
2774 b_tmpvar
2775 )) {
2776 return false;
2777 }
2778 if (!handle_binary_operation_assignment(
2779 expression.get_source_ref(),
2780 Operation::OP_ASSIGN,
2781 returned_tmpvar,
2782 a_tmpvar,
2783 arithmetic_tmpvar
2784 )) {
2785 return false;
2786 }
2787 }
2788 else if (op_type == ExpressionBinary::XOR_ASSIGNMENT) {
2789 // This is just syntax sugar for ^ followed by =
2790 size_t arithmetic_tmpvar;
2791 if (!handle_binary_operation_bitwise(
2792 expression.get_source_ref(),
2793 Operation::OP_BITWISE_XOR,
2794 arithmetic_tmpvar,
2795 a_tmpvar,
2796 b_tmpvar
2797 )) {
2798 return false;
2799 }
2800 if (!handle_binary_operation_assignment(
2801 expression.get_source_ref(),
2802 Operation::OP_ASSIGN,
2803 returned_tmpvar,
2804 a_tmpvar,
2805 arithmetic_tmpvar
2806 )) {
2807 return false;
2808 }
2809 }
2810 else {
2811 compiler_context
2812 .get_errors()
2813 .add_simple_error(
2814 expression.get_source_ref(),
2815 "Compiler bug! unknown binary operator",
2816 std::string("Invalid binary operator type ") + std::to_string(op_type)
2817 );
2818 return false;
2819 }
2820 646 return true;
2821 }
2822 bool
2823 FunctionDefinitionLowering::extract_from_expression_trinary(
2824 size_t & returned_tmpvar,
2825 const ExpressionTrinary & expression)
2826 {
2827 fprintf(stderr, "TODO: Trinary expressions are not yet supported.\n");
2828 return false;
2829 }
2830 bool
2831 FunctionDefinitionLowering::extract_from_expression_cast(
2832 size_t & returned_tmpvar,
2833 const ExpressionCast & expression)
2834 {
2835 fprintf(stderr, "TODO: Cast expressions are not yet supported.\n");
2836 return false;
2837 }
2838
2839
2840 bool
2841 2284 FunctionDefinitionLowering::extract_from_expression(
2842 size_t & returned_tmpvar,
2843 const Expression & expression_container)
2844 {
2845 2284 const auto & expression_type = expression_container.get_expression();
2846
2847
2/2
✓ Branch 1 taken 1254 times.
✓ Branch 2 taken 1030 times.
2284 if (std::holds_alternative<Gyoji::owned<ExpressionPrimaryIdentifier>>(expression_type)) {
2848 1254 const auto & expression = std::get<Gyoji::owned<ExpressionPrimaryIdentifier>>(expression_type);
2849 1254 return extract_from_expression_primary_identifier(returned_tmpvar, *expression);
2850 }
2851
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1026 times.
1030 else if (std::holds_alternative<Gyoji::owned<ExpressionPrimaryNested>>(expression_type)) {
2852 4 const auto & expression = std::get<Gyoji::owned<ExpressionPrimaryNested>>(expression_type);
2853 4 return extract_from_expression_primary_nested(returned_tmpvar, *expression);
2854 }
2855
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1018 times.
1026 else if (std::holds_alternative<Gyoji::owned<ExpressionPrimaryLiteralChar>>(expression_type)) {
2856 8 const auto & expression = std::get<Gyoji::owned<ExpressionPrimaryLiteralChar>>(expression_type);
2857 8 return extract_from_expression_primary_literal_char(returned_tmpvar, *expression);
2858 }
2859
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 1006 times.
1018 else if (std::holds_alternative<Gyoji::owned<ExpressionPrimaryLiteralString>>(expression_type)) {
2860 12 const auto & expression = std::get<Gyoji::owned<ExpressionPrimaryLiteralString>>(expression_type);
2861 12 return extract_from_expression_primary_literal_string(returned_tmpvar, *expression);
2862 }
2863
2/2
✓ Branch 1 taken 242 times.
✓ Branch 2 taken 764 times.
1006 else if (std::holds_alternative<Gyoji::owned<ExpressionPrimaryLiteralInt>>(expression_type)) {
2864 242 const auto & expression = std::get<Gyoji::owned<ExpressionPrimaryLiteralInt>>(expression_type);
2865 242 return extract_from_expression_primary_literal_int(returned_tmpvar, *expression);
2866 }
2867
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 758 times.
764 else if (std::holds_alternative<Gyoji::owned<ExpressionPrimaryLiteralFloat>>(expression_type)) {
2868 6 const auto & expression = std::get<Gyoji::owned<ExpressionPrimaryLiteralFloat>>(expression_type);
2869 6 return extract_from_expression_primary_literal_float(returned_tmpvar, *expression);
2870 }
2871
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 758 times.
758 else if (std::holds_alternative<Gyoji::owned<ExpressionPostfixArrayIndex>>(expression_type)) {
2872 const auto & expression = std::get<Gyoji::owned<ExpressionPostfixArrayIndex>>(expression_type);
2873 return extract_from_expression_postfix_array_index(returned_tmpvar, *expression);
2874 }
2875
2/2
✓ Branch 1 taken 84 times.
✓ Branch 2 taken 674 times.
758 else if (std::holds_alternative<Gyoji::owned<ExpressionPostfixFunctionCall>>(expression_type)) {
2876 84 const auto & expression = std::get<Gyoji::owned<ExpressionPostfixFunctionCall>>(expression_type);
2877 84 return extract_from_expression_postfix_function_call(returned_tmpvar, *expression);
2878 }
2879
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 672 times.
674 else if (std::holds_alternative<Gyoji::owned<ExpressionPostfixDot>>(expression_type)) {
2880 2 const auto & expression = std::get<Gyoji::owned<ExpressionPostfixDot>>(expression_type);
2881 2 return extract_from_expression_postfix_dot(returned_tmpvar, *expression);
2882 }
2883
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 672 times.
672 else if (std::holds_alternative<Gyoji::owned<ExpressionPostfixArrow>>(expression_type)) {
2884 const auto & expression = std::get<Gyoji::owned<ExpressionPostfixArrow>>(expression_type);
2885 return extract_from_expression_postfix_arrow(returned_tmpvar, *expression);
2886 }
2887
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 670 times.
672 else if (std::holds_alternative<Gyoji::owned<ExpressionPostfixIncDec>>(expression_type)) {
2888 2 const auto & expression = std::get<Gyoji::owned<ExpressionPostfixIncDec>>(expression_type);
2889 2 return extract_from_expression_postfix_incdec(returned_tmpvar, *expression);
2890 }
2891
2/2
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 654 times.
670 else if (std::holds_alternative<Gyoji::owned<ExpressionUnaryPrefix>>(expression_type)) {
2892 16 const auto & expression = std::get<Gyoji::owned<ExpressionUnaryPrefix>>(expression_type);
2893 16 return extract_from_expression_unary_prefix(returned_tmpvar, *expression);
2894 }
2895
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 646 times.
654 else if (std::holds_alternative<Gyoji::owned<ExpressionUnarySizeofType>>(expression_type)) {
2896 8 const auto & expression = std::get<Gyoji::owned<ExpressionUnarySizeofType>>(expression_type);
2897 8 return extract_from_expression_unary_sizeof_type(returned_tmpvar, *expression);
2898 }
2899
1/2
✓ Branch 1 taken 646 times.
✗ Branch 2 not taken.
646 else if (std::holds_alternative<Gyoji::owned<ExpressionBinary>>(expression_type)) {
2900 646 const auto & expression = std::get<Gyoji::owned<ExpressionBinary>>(expression_type);
2901 646 return extract_from_expression_binary(returned_tmpvar, *expression);
2902 }
2903 else if (std::holds_alternative<Gyoji::owned<ExpressionTrinary>>(expression_type)) {
2904 const auto & expression = std::get<Gyoji::owned<ExpressionTrinary>>(expression_type);
2905 return extract_from_expression_trinary(returned_tmpvar, *expression);
2906 }
2907 else if (std::holds_alternative<Gyoji::owned<ExpressionCast>>(expression_type)) {
2908 const auto & expression = std::get<Gyoji::owned<ExpressionCast>>(expression_type);
2909 return extract_from_expression_cast(returned_tmpvar, *expression);
2910 }
2911 else {
2912 fprintf(stderr, "Compiler bug, invalid expression type\n");
2913 return false;
2914 }
2915 }
2916
2917 bool
2918 312 FunctionDefinitionLowering::local_declare_or_error(
2919 const Gyoji::mir::Type *mir_type,
2920 const std::string & name,
2921 const SourceReference & source_ref
2922 )
2923 {
2924 312 scope_tracker.add_variable(name, mir_type, source_ref);
2925
2926 624 function->add_operation(
2927 current_block,
2928 624 std::make_unique<OperationLocalDeclare>(
2929 source_ref,
2930 name,
2931 mir_type
2932 )
2933 );
2934
2935 312 return true;
2936 }
2937
2938 bool
2939 6 FunctionDefinitionLowering::extract_from_struct_initializer(
2940 size_t & initial_value_tmpvar,
2941 const Gyoji::frontend::tree::StructInitializerExpression & struct_initializer_expression
2942 )
2943 {
2944 6 const auto & field_list = struct_initializer_expression.get_field_list();
2945 6 const auto & fields = field_list.get_fields();
2946
2947 6 std::vector<std::pair<std::string, size_t>> field_values;
2948 6 bool is_ok = true;
2949 6 std::vector<std::string> field_types;
2950 6 std::vector<TypeMember> members;
2951 6 size_t index = 0;
2952
2/2
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 6 times.
16 for (const auto & field : fields) {
2953 10 std::string name = field->get_identifier().get_name();
2954 size_t tmpvar;
2955
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
10 if (!extract_from_expression(
2956 tmpvar,
2957 field->get_expression())) {
2958 is_ok = false;
2959 }
2960 else {
2961 10 const Type *member_type = function->tmpvar_get(tmpvar);
2962 10 field_types.push_back(member_type->get_name());
2963 10 field_values.push_back(std::pair(name, tmpvar));
2964 10 members.push_back(
2965 20 TypeMember(
2966 name,
2967 index,
2968 member_type,
2969 10 field->get_identifier().get_source_ref()
2970 )
2971 );
2972 }
2973 10 index++;
2974 10 }
2975
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!is_ok) {
2976 return false;
2977 }
2978
2979 12 std::string field_desc_str = Gyoji::misc::join(field_types, ", ");
2980 18 std::string anonymous_structure_type_name = std::string("<anonymous_structure>{") + field_desc_str + std::string("}");
2981 6 Type *anonymous_structure_type = type_lowering.get_or_create(anonymous_structure_type_name, Type::TYPE_ANONYMOUS_STRUCTURE, false, struct_initializer_expression.get_source_ref());
2982
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
6 if (!anonymous_structure_type->is_complete()) {
2983 4 std::map<std::string, TypeMethod> methods; // No methods for anonymous types.
2984 4 anonymous_structure_type->complete_composite_definition(
2985 members,
2986 methods,
2987 struct_initializer_expression.get_source_ref()
2988 );
2989 4 }
2990
2991 // We model this as a 'void' type.
2992 6 initial_value_tmpvar = function->tmpvar_define(anonymous_structure_type);
2993 12 function->add_operation(
2994 current_block,
2995 12 std::make_unique<OperationAnonymousStructure>(
2996 struct_initializer_expression.get_source_ref(),
2997 initial_value_tmpvar,
2998 field_values
2999 )
3000 );
3001
3002 6 return true;
3003 6 }
3004
3005
3006 bool
3007 310 FunctionDefinitionLowering::extract_from_statement_variable_declaration(
3008 const StatementVariableDeclaration & statement
3009 )
3010 {
3011 // TODO:
3012 // For arrays, it is at this point that we define a type
3013 // for the 'array' so we can make it the correct size based on the literal given
3014 // for the 'opt_array' stuff.
3015
3016 // Once the variable exists, we can start performing the initialization
3017 // and assigning the value to something.
3018 310 const Gyoji::mir::Type * mir_type = type_lowering.extract_from_type_specifier(statement.get_type_specifier());
3019
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 310 times.
310 if (mir_type == nullptr) {
3020 return false;
3021 }
3022 620 if (!local_declare_or_error(
3023 mir_type,
3024
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 310 times.
620 statement.get_identifier().get_name(),
3025 310 statement.get_identifier().get_source_ref()
3026 )) {
3027 return false;
3028 }
3029
3030 // We don't assign here because we want the compiler
3031 // to detect lack of initialization.
3032 const SourceReference * source_ref;
3033
3034 // From here, we need to:
3035 // 1) call LocalVariable
3036 // 2) Evaluate the expression
3037 // 3) Perform an assignment.
3038 310 size_t variable_tmpvar = function->tmpvar_define(mir_type);
3039 620 function->add_operation(
3040 current_block,
3041 620 std::make_unique<OperationLocalVariable>(
3042 statement.get_source_ref(),
3043 variable_tmpvar,
3044 620 statement.get_identifier().get_name(),
3045 mir_type
3046 )
3047 );
3048 size_t initial_value_tmpvar;
3049
3050 310 const auto & initializer_expression = statement.get_initializer_expression();
3051
3052 // This is an equals-style
3053 // initialization of a primitive variable.
3054
5/6
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 304 times.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 304 times.
310 if (mir_type->is_composite() && initializer_expression.has_struct_expression()) {
3055
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if (!extract_from_struct_initializer(
3056 initial_value_tmpvar,
3057 initializer_expression.get_struct_initializer_expression()
3058 )) {
3059 return false;
3060 }
3061 6 source_ref = &initializer_expression.get_struct_initializer_expression().get_source_ref();
3062
3063 6 const Type *initializer_type = function->tmpvar_get(initial_value_tmpvar);
3064 6 const std::vector<TypeMember> & lhs_members = mir_type->get_members();
3065 6 const std::vector<TypeMember> & rhs_members = initializer_type->get_members();
3066
3067 6 std::set<std::string> lhs_vars;
3068 6 std::set<std::string> rhs_vars;
3069
2/2
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 6 times.
16 for (const TypeMember & m : lhs_members) {
3070 10 lhs_vars.insert(m.get_name());
3071 }
3072
2/2
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 6 times.
16 for (const TypeMember & m : rhs_members) {
3073 10 rhs_vars.insert(m.get_name());
3074 }
3075 6 std::vector<std::string> lhs_uninitialized;
3076 6 std::vector<std::string> rhs_unused;
3077 6 std::set_difference(
3078 lhs_vars.begin(), lhs_vars.end(),
3079 rhs_vars.begin(), rhs_vars.end(),
3080 std::back_inserter(lhs_uninitialized)
3081 );
3082 6 std::set_difference(
3083 rhs_vars.begin(), rhs_vars.end(),
3084 lhs_vars.begin(), lhs_vars.end(),
3085 std::back_inserter(rhs_unused)
3086 );
3087 6 bool is_ok = true;
3088
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
6 for (const auto & s : lhs_uninitialized) {
3089 const TypeMember *m = mir_type->member_get(s);
3090
3091 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Uninitialized class member");
3092 error->add_message(
3093 initializer_expression.get_source_ref(),
3094 std::string("Class member ") + s + std::string(" is missing from initializer")
3095 );
3096 error->add_message(
3097 m->get_source_ref(),
3098 "Member declared here"
3099 );
3100 compiler_context
3101 .get_errors()
3102 .add_error(std::move(error));
3103 is_ok = false;
3104 }
3105
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
6 for (const auto & s : rhs_unused) {
3106 const TypeMember *m = initializer_type->member_get(s);
3107 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Unused Initializer");
3108 error->add_message(
3109 m->get_source_ref(),
3110 std::string("Initializer ") + s + std::string(" is not a member of class ") + mir_type->get_name()
3111 );
3112 error->add_message(
3113 mir_type->get_defined_source_ref(),
3114 "Class declared here"
3115 );
3116 compiler_context
3117 .get_errors()
3118 .add_error(std::move(error));
3119 is_ok = false;
3120 }
3121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!is_ok) {
3122 return false;
3123 }
3124
4/8
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 6 times.
✗ Branch 11 not taken.
6 }
3125 else {
3126
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 294 times.
304 if (initializer_expression.has_expression()) {
3127
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
10 if (!extract_from_expression(
3128 initial_value_tmpvar,
3129 initializer_expression.get_expression())) {
3130 return false;
3131 }
3132 10 source_ref = &initializer_expression.get_expression().get_source_ref();
3133 }
3134
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 294 times.
294 else if (initializer_expression.has_struct_expression()) {
3135 // This isn't valid for primitive types.
3136 compiler_context
3137 .get_errors()
3138 .add_simple_error(
3139 initializer_expression.get_source_ref(),
3140 "Primitive types may not be initialized with structures",
3141 "Primitive types may not be initialized with structures, but must be initialized with other primitive values"
3142 );
3143 return false;
3144 }
3145 else {
3146 // The author has chosen to defer initialization,
3147 // so we're done with the initialization part.
3148 // We just have to trust that the author will initialize
3149 // later.
3150 294 return true;
3151 }
3152 }
3153 size_t returned_tmpvar; // We don't save the returned val because nobody wants it.
3154
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 if (!handle_binary_operation_assignment(
3155 *source_ref,
3156 Operation::OP_ASSIGN,
3157 returned_tmpvar,
3158 variable_tmpvar,
3159 initial_value_tmpvar
3160 )) {
3161 return false;
3162 }
3163
3164 16 return true;
3165 }
3166
3167 bool
3168 36 FunctionDefinitionLowering::extract_from_statement_ifelse(
3169 const StatementIfElse & statement
3170 )
3171 {
3172 size_t condition_tmpvar;
3173
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 36 times.
36 if (!extract_from_expression(condition_tmpvar, statement.get_expression())) {
3174 return false;
3175 }
3176 // First, evaluate the expression to get our condition.
3177
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 36 times.
36 if (!function->tmpvar_get(condition_tmpvar)->is_bool()) {
3178 compiler_context
3179 .get_errors()
3180 .add_simple_error(
3181 statement.get_expression().get_source_ref(),
3182 "Invalid condition in if statement.",
3183 std::string("Type of condition expression should be 'bool' and was ") + function->tmpvar_get(condition_tmpvar)->get_name()
3184 );
3185 return false;
3186 }
3187
3188 36 size_t blockid_if = function->add_block();
3189 36 size_t blockid_done = function->add_block();
3190 36 size_t blockid_else = -1;
3191
5/6
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 28 times.
✓ Branch 6 taken 8 times.
✓ Branch 7 taken 28 times.
36 if (statement.has_else() || statement.has_else_if()) {
3192 // If we have an 'else', then
3193 // dump to it based on the condition.
3194 8 blockid_else = function->add_block();
3195 16 function->add_operation(
3196 current_block,
3197 16 std::make_unique<OperationJumpConditional>(
3198 statement.get_source_ref(),
3199 condition_tmpvar,
3200 blockid_if,
3201 blockid_else
3202 )
3203 );
3204 }
3205 else {
3206 // Otherwise, jump to done
3207 // based on condition.
3208 56 function->add_operation(
3209 current_block,
3210 56 std::make_unique<OperationJumpConditional>(
3211 statement.get_source_ref(),
3212 condition_tmpvar,
3213 blockid_if,
3214 blockid_done
3215 )
3216 );
3217 }
3218
3219 36 current_block = blockid_if;
3220
3221 // Perform the stuff inside the 'if' block.
3222 // If blocks cannot inherently be defined as unsafe,
3223 // so if you need that, put the if statement inside
3224 // an unsafe block.
3225 36 scope_tracker.scope_push(false, statement.get_if_scope_body().get_source_ref());
3226
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 36 times.
36 if (!extract_from_statement_list(
3227 true,
3228 36 statement.get_if_scope_body().get_statements()
3229 )) {
3230 return false;
3231 }
3232 36 scope_tracker.scope_pop();
3233
3234 // Unconditionally jump to done
3235 // unless the block is alreayd terminated by
3236 // another jump or a return.
3237
2/2
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 24 times.
36 if (!function->get_basic_block(current_block).contains_terminator()) {
3238 24 function->add_operation(
3239 current_block,
3240 24 std::make_unique<OperationJump>(
3241 statement.get_source_ref(),
3242 blockid_done
3243 )
3244 );
3245 }
3246
3247
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 28 times.
36 if (statement.has_else()) {
3248 // Perform the stuff in the 'else' block.
3249 // If/Else blocks cannot inherently be defined as unsafe,
3250 // so if you need that, put the if statement inside
3251 // an unsafe block.
3252 8 scope_tracker.scope_push(false, statement.get_else_scope_body().get_source_ref());
3253 8 size_t blockid_tmp = current_block;
3254 8 current_block = blockid_else;
3255
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if (!extract_from_statement_list(
3256 true,
3257 8 statement.get_else_scope_body().get_statements()
3258 )) {
3259 return false;
3260 }
3261 8 current_block = blockid_tmp;
3262 8 scope_tracker.scope_pop();
3263 // Jump to the 'done' block when the 'else' block is finished
3264 // unless it has terminated already.
3265
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 if (!function->get_basic_block(blockid_else).contains_terminator()) {
3266 16 function->add_operation(
3267 blockid_else,
3268 16 std::make_unique<OperationJump>(
3269 statement.get_source_ref(),
3270 blockid_done
3271 )
3272 );
3273 }
3274 }
3275
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
28 else if (statement.has_else_if()) {
3276 if (!extract_from_statement_ifelse(
3277 statement.get_else_if()
3278 )) {
3279 return false;
3280 }
3281 }
3282 36 current_block = blockid_done;
3283 36 return true;
3284 }
3285
3286 bool
3287 2 FunctionDefinitionLowering::extract_from_statement_while(
3288 const Gyoji::frontend::tree::StatementWhile & statement
3289 )
3290 {
3291 size_t condition_tmpvar;
3292
3293 2 size_t blockid_evaluate_expression = function->add_block();
3294 2 size_t blockid_if = function->add_block();
3295 2 size_t blockid_done = function->add_block();
3296
3297 4 function->add_operation(
3298 current_block,
3299 4 std::make_unique<OperationJump>(
3300 statement.get_source_ref(),
3301 blockid_evaluate_expression
3302 )
3303 );
3304
3305 2 current_block = blockid_evaluate_expression;
3306
3307
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_expression(condition_tmpvar, statement.get_expression())) {
3308 return false;
3309 }
3310
3311 4 function->add_operation(
3312 current_block,
3313 4 std::make_unique<OperationJumpConditional>(
3314 statement.get_source_ref(),
3315 condition_tmpvar,
3316 blockid_if,
3317 blockid_done
3318 )
3319 );
3320
3321 // Push a loop scope.
3322 2 current_block = blockid_if;
3323 2 scope_tracker.scope_push_loop(
3324 2 statement.get_scope_body().get_source_ref(),
3325 blockid_done,
3326 blockid_evaluate_expression
3327 );
3328
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_statement_list(
3329 true,
3330 2 statement.get_scope_body().get_statements()
3331 )) {
3332 return false;
3333 }
3334 // Pop back from the scope.
3335 2 scope_tracker.scope_pop();
3336
3337 4 function->add_operation(
3338 current_block,
3339 4 std::make_unique<OperationJump>(
3340 statement.get_source_ref(),
3341 blockid_evaluate_expression
3342 )
3343 );
3344
3345 2 current_block = blockid_done;
3346
3347 2 return true;
3348 }
3349
3350 bool
3351 2 FunctionDefinitionLowering::extract_from_statement_for(
3352 const Gyoji::frontend::tree::StatementFor & statement
3353 )
3354 {
3355 size_t condition_tmpvar;
3356
3357 2 size_t blockid_evaluate_expression_termination = function->add_block();
3358 2 size_t blockid_if = function->add_block();
3359 2 size_t blockid_done = function->add_block();
3360
3361
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (statement.is_declaration()) {
3362 2 const Gyoji::mir::Type * mir_type = type_lowering.extract_from_type_specifier(statement.get_type_specifier());
3363
3364 4 if (!local_declare_or_error(
3365 mir_type,
3366
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
4 statement.get_identifier().get_name(),
3367 2 statement.get_identifier().get_source_ref()
3368 )) {
3369 return false;
3370 }
3371
3372 2 size_t variable_tmpvar = function->tmpvar_define(mir_type);
3373 4 function->add_operation(
3374 current_block,
3375 4 std::make_unique<OperationLocalVariable>(
3376 2 statement.get_identifier().get_source_ref(),
3377 variable_tmpvar,
3378 4 statement.get_identifier().get_name(),
3379 mir_type
3380 )
3381 );
3382
3383 size_t initializer_tmpvar;
3384
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_expression(initializer_tmpvar, statement.get_expression_initial())) {
3385 return false;
3386 }
3387
3388 size_t ignore_tmpvar;
3389
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!handle_binary_operation_assignment(
3390 2 statement.get_identifier().get_source_ref(),
3391 Operation::OP_ASSIGN,
3392 ignore_tmpvar,
3393 variable_tmpvar,
3394 initializer_tmpvar)) {
3395 return false;
3396 }
3397 }
3398 else {
3399 // Evaluate the initialization expression
3400 size_t initializer_tmpvar;
3401 if (!extract_from_expression(initializer_tmpvar, statement.get_expression_initial())) {
3402 return false;
3403 }
3404 }
3405
3406 4 function->add_operation(
3407 current_block,
3408 4 std::make_unique<OperationJump>(
3409 statement.get_source_ref(),
3410 blockid_evaluate_expression_termination
3411 )
3412 );
3413
3414 // Evaluate the termination condition.
3415 2 size_t blockid_tmp = current_block;
3416 2 current_block = blockid_evaluate_expression_termination;
3417
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_expression(condition_tmpvar, statement.get_expression_termination())) {
3418 return false;
3419 }
3420 2 current_block = blockid_tmp;
3421
3422 4 function->add_operation(
3423 blockid_evaluate_expression_termination,
3424 4 std::make_unique<OperationJumpConditional>(
3425 statement.get_source_ref(),
3426 condition_tmpvar,
3427 blockid_if,
3428 blockid_done
3429 )
3430 );
3431
3432 2 scope_tracker.scope_push_loop(
3433 2 statement.get_scope_body().get_source_ref(),
3434 blockid_done,
3435 blockid_evaluate_expression_termination
3436 );
3437
3438 2 size_t blockid_tmp_if = current_block;
3439 2 current_block = blockid_if;
3440
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_statement_list(
3441 true,
3442 2 statement.get_scope_body().get_statements()
3443 )) {
3444 return false;
3445 }
3446
3447 2 scope_tracker.scope_pop();
3448
3449 // Evaluate the 'increment' expression
3450
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_expression(condition_tmpvar, statement.get_expression_increment())) {
3451 return false;
3452 }
3453 2 current_block = blockid_tmp_if;
3454
3455 4 function->add_operation(
3456 blockid_if,
3457 4 std::make_unique<OperationJump>(
3458 statement.get_source_ref(),
3459 blockid_evaluate_expression_termination
3460 )
3461 );
3462
3463 2 current_block = blockid_done;
3464
3465 2 return true;
3466 }
3467
3468 bool
3469 FunctionDefinitionLowering::extract_from_statement_switch(
3470 const Gyoji::frontend::tree::StatementSwitch & statement
3471 )
3472 {
3473
3474 // Extract the value of the expression
3475 // we will be testing.
3476 size_t switch_value_tmpvar;
3477 if (!extract_from_expression(switch_value_tmpvar, statement.get_expression())) {
3478 return false;
3479 }
3480 const Type *switch_value_type = function->tmpvar_get(switch_value_tmpvar);
3481
3482 size_t blockid_done = function->add_block();
3483
3484 bool is_ok = true;
3485 const StatementSwitchContent & switch_content = statement.get_switch_content();
3486
3487 size_t blockid_else;
3488
3489 const auto & blocks = switch_content.get_blocks();
3490 size_t nblocks = blocks.size();
3491 size_t i = 0;
3492
3493 bool has_default = false;
3494 for (const auto & block_ptr : blocks) {
3495 if (block_ptr->is_default()) {
3496 if (i != nblocks-1) {
3497 compiler_context
3498 .get_errors()
3499 .add_simple_error(
3500 block_ptr->get_source_ref(),
3501 "Default clause must be the last clause in a switch statement.",
3502 std::string("Default clause must be the last clause in a switch statement.")
3503 );
3504 return false;
3505 }
3506 has_default = true;
3507 blockid_else = blockid_done;
3508 }
3509 else {
3510 size_t test_value_tmpvar;
3511 if (!extract_from_expression(test_value_tmpvar, block_ptr->get_expression())) {
3512 return false;
3513 }
3514 const Type *test_value_type = function->tmpvar_get(test_value_tmpvar);
3515 if (test_value_type->get_name() != switch_value_type->get_name()) {
3516 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Case must match switch type");
3517 error->add_message(
3518 block_ptr->get_source_ref(),
3519 std::string("Case type ") + test_value_type->get_name() + std::string(" must match switch type ") + switch_value_type->get_name()
3520 );
3521 error->add_message(
3522 statement.get_expression().get_source_ref(),
3523 "Switch declared here."
3524 );
3525 compiler_context
3526 .get_errors()
3527 .add_error(std::move(error));
3528 is_ok = false;
3529 }
3530 size_t condition_tmpvar = function->tmpvar_define(mir.get_types().get_type("bool"));
3531 function->add_operation(
3532 current_block,
3533 std::make_unique<OperationBinary>(
3534 Operation::OP_COMPARE_EQUAL,
3535 block_ptr->get_source_ref(),
3536 condition_tmpvar,
3537 test_value_tmpvar,
3538 switch_value_tmpvar
3539 )
3540 );
3541
3542 size_t blockid_if = function->add_block();
3543 blockid_else = function->add_block();
3544 function->add_operation(
3545 current_block,
3546 std::make_unique<OperationJumpConditional>(
3547 block_ptr->get_source_ref(),
3548 condition_tmpvar,
3549 blockid_if,
3550 blockid_else
3551 )
3552 );
3553
3554 current_block = blockid_if;
3555 }
3556
3557 // Switch blocks are not inherently unsafe.
3558 // If you need that, you need to declare the unsafe block
3559 // either inside the block or outside the switch.
3560 scope_tracker.scope_push(false, block_ptr->get_scope_body().get_source_ref());
3561 if (!extract_from_statement_list(true, block_ptr->get_scope_body().get_statements())) {
3562 return false;
3563 }
3564 scope_tracker.scope_pop();
3565
3566 // If we have returned from the block,
3567 // then it's safe to leave off the jump back
3568 // to outside the switch.
3569 if (!function->get_basic_block(current_block).contains_terminator()) {
3570 function->add_operation(
3571 current_block,
3572 std::make_unique<OperationJump>(
3573 block_ptr->get_source_ref(),
3574 blockid_done
3575 )
3576 );
3577 }
3578 current_block = blockid_else;
3579
3580 i++;
3581 }
3582
3583 // If there is no default clause, we add an 'empty' default
3584 // clause that does nothing.
3585 current_block = blockid_else;
3586 if (!has_default) {
3587 // This just handles the end case where there are no more
3588 // conditions, so we unconditionaly jump back to done.
3589 function->add_operation(
3590 current_block,
3591 std::make_unique<OperationJump>(
3592 statement.get_source_ref(),
3593 blockid_done
3594 )
3595 );
3596 current_block = blockid_done;
3597 }
3598
3599 return is_ok;
3600 }
3601
3602
3603 bool
3604 2 FunctionDefinitionLowering::extract_from_statement_break(
3605 const Gyoji::frontend::tree::StatementBreak & statement
3606 )
3607 {
3608
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!scope_tracker.is_in_loop()) {
3609 compiler_context
3610 .get_errors()
3611 .add_simple_error(statement.get_source_ref(),
3612 "'break' statement not in loop or switch statement",
3613 "'break' keyword must appear inside a loop (for/while)"
3614 );
3615 return true;
3616 }
3617
3618 2 std::vector<std::string> unwind_break = scope_tracker.get_variables_to_unwind_for_break();
3619 2 leave_scope(unwind_break, statement.get_source_ref());
3620
3621 4 function->add_operation(
3622 current_block,
3623 4 std::make_unique<OperationJump>(
3624 statement.get_source_ref(),
3625 2 scope_tracker.get_loop_break_blockid()
3626 )
3627 );
3628
3629 2 return true;
3630 2 }
3631
3632 bool
3633 2 FunctionDefinitionLowering::extract_from_statement_continue(
3634 const Gyoji::frontend::tree::StatementContinue & statement
3635 )
3636 {
3637
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!scope_tracker.is_in_loop()) {
3638 compiler_context
3639 .get_errors()
3640 .add_simple_error(statement.get_source_ref(),
3641 "'continue' statement not in loop or switch statement",
3642 "'continue' keyword must appear inside a loop (for/while)"
3643 );
3644 return true;
3645 }
3646 4 function->add_operation(
3647 current_block,
3648 4 std::make_unique<OperationJump>(
3649 statement.get_source_ref(),
3650 2 scope_tracker.get_loop_continue_blockid()
3651 )
3652 );
3653
3654 2 return true;
3655 }
3656
3657
3658 bool
3659 2 FunctionDefinitionLowering::extract_from_statement_label(
3660 const Gyoji::frontend::tree::StatementLabel & statement
3661 )
3662 {
3663 // We're starting a new label, so this is, by definition,
3664 // a new basic block. This means we need to create a new
3665 // block and issue a 'jump' to it.
3666
3667 2 const std::string & label_name = statement.get_name();
3668 size_t label_block;
3669
3670 2 const FunctionLabel *label = scope_tracker.get_label(label_name);
3671
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (label == nullptr) {
3672 2 label_block = function->add_block();
3673 2 scope_tracker.label_define(label_name, label_block, statement.get_name_source_ref());
3674 }
3675 else {
3676 if (!label->is_resolved()) {
3677 scope_tracker.label_define(label_name, statement.get_name_source_ref());
3678 label_block = label->get_block();
3679 }
3680 else {
3681 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Labels in functions must be unique");
3682 error->add_message(statement.get_name_source_ref(),
3683 std::string("Duplicate label ") + label_name);
3684 error->add_message(label->get_source_ref(),
3685 "First declared here.");
3686 compiler_context
3687 .get_errors()
3688 .add_error(std::move(error));
3689 return true;
3690 }
3691 }
3692 4 function->add_operation(
3693 current_block,
3694 4 std::make_unique<OperationJump>(
3695 statement.get_source_ref(),
3696 label_block
3697 )
3698 );
3699 // Then whatever we add next will be in this new block.
3700 2 current_block = label_block;
3701
3702 2 return true;
3703 }
3704
3705 bool
3706 2 FunctionDefinitionLowering::extract_from_statement_goto(
3707 const Gyoji::frontend::tree::StatementGoto & statement
3708 )
3709 {
3710 2 const std::string & label_name = statement.get_label();
3711
3712 2 const FunctionLabel *label = scope_tracker.get_label(label_name);
3713 size_t label_block;
3714
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (label == nullptr) {
3715 label_block = function->add_block();
3716 scope_tracker.label_declare(label_name, label_block);
3717 }
3718 else {
3719 2 label_block = label->get_block();
3720 }
3721
3722 // We need to track what point
3723 // in the MIR the goto appears so that
3724 // we can insert the unwindings before that point.
3725 2 Gyoji::owned<FunctionPoint> function_point = std::make_unique<FunctionPoint>(current_block, function->get_basic_block(current_block).size());
3726 2 scope_tracker.add_goto(label_name, std::move(function_point), statement.get_label_source_ref());
3727
3728 4 function->add_operation(
3729 current_block,
3730 4 std::make_unique<OperationJump>(
3731 statement.get_source_ref(),
3732 label_block
3733 )
3734 );
3735 // This jump ends the basic block, so it's terminated.
3736 4 return true;
3737 2 }
3738
3739 bool
3740 284 FunctionDefinitionLowering::extract_from_statement_return(
3741 const StatementReturn & statement
3742 )
3743 {
3744 284 std::vector<std::string> unwind_root = scope_tracker.get_variables_to_unwind_for_root();
3745
3746
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 280 times.
284 if (statement.is_void()) {
3747 4 leave_scope(unwind_root, statement.get_source_ref());
3748 8 function->add_operation(
3749 current_block,
3750 8 std::make_unique<OperationReturnVoid>(
3751 statement.get_source_ref()
3752 )
3753 );
3754 }
3755 else {
3756 size_t expression_tmpvar;
3757
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 280 times.
280 if (!extract_from_expression(expression_tmpvar, statement.get_expression())) {
3758 return false;
3759 }
3760 280 leave_scope(unwind_root, statement.get_source_ref());
3761 560 function->add_operation(
3762 current_block,
3763 560 std::make_unique<OperationReturn>(
3764 statement.get_source_ref(),
3765 expression_tmpvar
3766 )
3767 );
3768 }
3769 284 return true;
3770 284 }
3771
3772 size_t
3773 904 FunctionDefinitionLowering::undeclare_local(
3774 const std::string & variable_name,
3775 const Type *class_type,
3776 size_t basic_block_id,
3777 size_t location,
3778 const SourceReference & src_ref
3779 )
3780 {
3781 904 size_t n = 0;
3782
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 898 times.
904 if (class_type->is_composite()) {
3783 // Look to see if a destructor is declared.
3784 6 fprintf(stderr, "Looking for destructor for %s\n", class_type->get_name().c_str());
3785
2/2
✓ Branch 6 taken 10 times.
✓ Branch 7 taken 6 times.
16 for (const auto & method : class_type->get_methods()) {
3786 10 fprintf(stderr, " Method %s\n", method.first.c_str());
3787 }
3788 // Where to find the leaf-node "Foo" part of the class name????
3789 12 std::string fully_qualified_function_name(class_type->get_name() + std::string("::~") + class_type->get_simple_name());
3790
3791 6 const Gyoji::mir::Symbol *symbol = mir.get_symbols().get_symbol(fully_qualified_function_name);
3792
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (symbol != nullptr) {
3793 6 fprintf(stderr, "Found destructor\n");
3794 6 const Type *destructor_fptr_type = symbol->get_mir_type();
3795
3796 // Destructors take no arguments except the
3797 // object itself.
3798 6 std::vector<size_t> passed_arguments;
3799
3800 6 size_t variable_tmpvar = function->tmpvar_define(class_type);
3801 12 function->insert_operation(
3802 basic_block_id,
3803 location,
3804 12 std::make_unique<OperationLocalVariable>(
3805 src_ref,
3806 variable_tmpvar,
3807 variable_name,
3808 class_type
3809 )
3810 );
3811 6 location++;
3812 6 n++;
3813
3814 6 const Type *variable_pointer_type = mir.get_types().get_pointer_to(class_type, src_ref);
3815 6 size_t variable_pointer_tmpvar = function->tmpvar_define(variable_pointer_type);
3816 12 function->insert_operation(
3817 basic_block_id,
3818 location,
3819 6 std::make_unique<OperationUnary>(
3820 6 Operation::OP_ADDRESSOF,
3821 src_ref,
3822 variable_pointer_tmpvar,
3823 variable_tmpvar
3824 )
3825 );
3826 6 location++;
3827 6 n++;
3828
3829 6 passed_arguments.push_back(variable_pointer_tmpvar);
3830
3831 // Emitting destructor call.
3832 6 size_t destructor_fptr_tmpvar = function->tmpvar_define(destructor_fptr_type);
3833 6 std::vector<size_t> partial_operands;
3834 12 function->insert_operation(
3835 basic_block_id,
3836 location,
3837 12 std::make_unique<OperationSymbol>(
3838 src_ref,
3839 destructor_fptr_tmpvar,
3840 partial_operands,
3841 fully_qualified_function_name
3842 )
3843 );
3844 6 location++;
3845 6 n++;
3846
3847 // This is what the function pointer type should return.
3848 6 size_t destructor_result_tmpvar = function->tmpvar_define(destructor_fptr_type->get_return_type());
3849 12 function->insert_operation(
3850 basic_block_id,
3851 location,
3852 6 std::make_unique<OperationFunctionCall>(
3853 6 Operation::OP_DESTRUCTOR,
3854 src_ref,
3855 destructor_result_tmpvar,
3856 destructor_fptr_tmpvar,
3857 passed_arguments
3858 )
3859 );
3860 6 location++;
3861 6 n++;
3862
3863 6 }
3864 6 }
3865
3866 1808 function->insert_operation(
3867 basic_block_id,
3868 location,
3869 1808 std::make_unique<OperationLocalUndeclare>(
3870 src_ref,
3871 variable_name
3872 )
3873 );
3874 904 location++;
3875 904 n++;
3876 904 return n;
3877 }
3878
3879 void
3880 322 FunctionDefinitionLowering::leave_scope(
3881 std::vector<std::string> & unwind,
3882 const SourceReference & src_ref
3883 )
3884 {
3885 322 const BasicBlock & block = function->get_basic_block(current_block);
3886 322 size_t location = block.get_operations().size();
3887
2/2
✓ Branch 4 taken 896 times.
✓ Branch 5 taken 322 times.
1218 for (const auto & variable_name : unwind) {
3888 896 const LocalVariable *localvar = scope_tracker.get_variable(variable_name);
3889 896 const Type *class_type = localvar->get_type();
3890 896 location += undeclare_local(variable_name, class_type, current_block, location, src_ref);
3891 }
3892 322 unwind.clear();
3893 322 }
3894
3895 bool
3896 326 FunctionDefinitionLowering::extract_from_statement_list(
3897 bool automatic_unwind,
3898 const StatementList & statement_list)
3899 {
3900
3901 326 bool current_block_terminated = false;
3902
2/2
✓ Branch 6 taken 1102 times.
✓ Branch 7 taken 326 times.
1428 for (const auto & statement_el : statement_list.get_statements()) {
3903 1102 const auto & statement_type = statement_el->get_statement();
3904
2/2
✓ Branch 1 taken 310 times.
✓ Branch 2 taken 792 times.
1102 if (std::holds_alternative<Gyoji::owned<StatementVariableDeclaration>>(statement_type)) {
3905 310 const auto & statement = std::get<Gyoji::owned<StatementVariableDeclaration>>(statement_type);
3906
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 310 times.
310 if (!extract_from_statement_variable_declaration(*statement)) {
3907 return false;
3908 }
3909 }
3910
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 784 times.
792 else if (std::holds_alternative<Gyoji::owned<StatementBlock>>(statement_type)) {
3911 8 const auto & statement = std::get<Gyoji::owned<StatementBlock>>(statement_type);
3912 16 scope_tracker.scope_push(
3913 8 statement->get_unsafe_modifier().is_unsafe(),
3914 8 statement->get_scope_body().get_source_ref()
3915 );
3916
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
8 if (!extract_from_statement_list(true, statement->get_scope_body().get_statements())) {
3917 return false;
3918 }
3919 8 scope_tracker.scope_pop();
3920 }
3921
2/2
✓ Branch 1 taken 452 times.
✓ Branch 2 taken 332 times.
784 else if (std::holds_alternative<Gyoji::owned<StatementExpression>>(statement_type)) {
3922 452 const auto & statement = std::get<Gyoji::owned<StatementExpression>>(statement_type);
3923 size_t returned_tmpvar;
3924
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 452 times.
452 if (!extract_from_expression(
3925 returned_tmpvar,
3926 statement->get_expression())) {
3927 return false;
3928 }
3929 }
3930
2/2
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 296 times.
332 else if (std::holds_alternative<Gyoji::owned<StatementIfElse>>(statement_type)) {
3931 36 const auto & statement = std::get<Gyoji::owned<StatementIfElse>>(statement_type);
3932
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
36 if (!extract_from_statement_ifelse(
3933 36 *statement)) {
3934 return false;
3935 }
3936 }
3937
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 294 times.
296 else if (std::holds_alternative<Gyoji::owned<StatementWhile>>(statement_type)) {
3938 2 const auto & statement = std::get<Gyoji::owned<StatementWhile>>(statement_type);
3939
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!extract_from_statement_while(
3940 2 *statement)) {
3941 return false;
3942 }
3943 }
3944
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 292 times.
294 else if (std::holds_alternative<Gyoji::owned<StatementFor>>(statement_type)) {
3945 2 const auto & statement = std::get<Gyoji::owned<StatementFor>>(statement_type);
3946
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!extract_from_statement_for(
3947 2 *statement)) {
3948 return false;
3949 }
3950 }
3951
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 292 times.
292 else if (std::holds_alternative<Gyoji::owned<StatementSwitch>>(statement_type)) {
3952 const auto & statement = std::get<Gyoji::owned<StatementSwitch>>(statement_type);
3953 if (!extract_from_statement_switch(
3954 *statement)) {
3955 return false;
3956 }
3957 }
3958
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 290 times.
292 else if (std::holds_alternative<Gyoji::owned<StatementLabel>>(statement_type)) {
3959 2 const auto & statement = std::get<Gyoji::owned<StatementLabel>>(statement_type);
3960
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_statement_label(*statement)) {
3961 return false;
3962 }
3963 }
3964
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 288 times.
290 else if (std::holds_alternative<Gyoji::owned<StatementGoto>>(statement_type)) {
3965 2 const auto & statement = std::get<Gyoji::owned<StatementGoto>>(statement_type);
3966
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!extract_from_statement_goto(*statement)) {
3967 return false;
3968 }
3969 2 current_block_terminated = true;
3970 }
3971
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 286 times.
288 else if (std::holds_alternative<Gyoji::owned<StatementBreak>>(statement_type)) {
3972 2 const auto & statement = std::get<Gyoji::owned<StatementBreak>>(statement_type);
3973
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!extract_from_statement_break(
3974 2 *statement)) {
3975 return false;
3976 }
3977 2 current_block_terminated = true;
3978 }
3979
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 284 times.
286 else if (std::holds_alternative<Gyoji::owned<StatementContinue>>(statement_type)) {
3980 2 const auto & statement = std::get<Gyoji::owned<StatementContinue>>(statement_type);
3981
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!extract_from_statement_continue(
3982 2 *statement)) {
3983 return false;
3984 }
3985 2 current_block_terminated = true;
3986 }
3987
1/2
✓ Branch 1 taken 284 times.
✗ Branch 2 not taken.
284 else if (std::holds_alternative<Gyoji::owned<StatementReturn>>(statement_type)) {
3988 284 const auto & statement = std::get<Gyoji::owned<StatementReturn>>(statement_type);
3989 // The return may need to unwind local declarations
3990 // and ensure destructors are called.
3991
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 284 times.
284 if (!extract_from_statement_return(*statement)) {
3992 return false;
3993 }
3994 284 current_block_terminated = true;
3995 }
3996 else {
3997 fprintf(stderr, "Compiler bug, invalid statement type\n");
3998 return false;
3999 }
4000 }
4001 // If we did not do a return, we should
4002 // unwind the current scope.
4003 // If we did a return already, we will already
4004 // have called these, so we should skip it.
4005 // Note that in the outer-most scope, we don't
4006 // do this because we'll add the return and
4007 // the scope unwinding based on the end of
4008 // the function (if it is reachable)
4009
4/4
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 290 times.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 4 times.
326 if (!current_block_terminated && automatic_unwind) {
4010 32 std::vector<std::string> unwind_scope = scope_tracker.get_variables_to_unwind_for_scope();
4011 // Get the type of the unwound variable. If it's a class, call the destructor.
4012 32 leave_scope(unwind_scope, statement_list.get_source_ref());
4013 32 }
4014
4015 326 return true;
4016 }
4017
4018