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