GCC Code Coverage Report


Directory: src/
File: src/frontend/type-lowering.cpp
Date: 2025-10-24 11:14:59
Exec Total Coverage
Lines: 252 378 66.7%
Functions: 19 22 86.4%
Branches: 82 126 65.1%

Line Branch Exec Source
1 /* Copyright 2025 Jonathan S. Arney
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * https://github.com/jarney/gyoji/blob/master/LICENSE
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include <gyoji-frontend/type-lowering.hpp>
16 #include <variant>
17 #include <stdio.h>
18 #include <gyoji-misc/jstring.hpp>
19
20 using namespace Gyoji::mir;
21 using namespace Gyoji::context;
22 using namespace Gyoji::frontend::tree;
23 using namespace Gyoji::frontend::lowering;
24 using namespace Gyoji::frontend::namespaces;
25
26 30 TypeLowering::TypeLowering(
27 Gyoji::context::CompilerContext & _compiler_context,
28 const Gyoji::frontend::tree::TranslationUnit & _translation_unit,
29 30 Gyoji::mir::MIR & _mir)
30 30 : mir(_mir)
31 30 , compiler_context(_compiler_context)
32 30 , translation_unit(_translation_unit)
33 30 {}
34 30 TypeLowering::~TypeLowering()
35 30 {}
36
37 30 void TypeLowering::lower()
38 {
39 // To resolve the types, we need only iterate the
40 // input parse tree and pull out any type declarations,
41 // resolving them down to their primitive types.
42 30 extract_types(translation_unit.get_statements());
43 30 }
44
45 void
46 8 TypeLowering::extract_from_class_declaration(const ClassDeclaration & declaration)
47 {
48 Gyoji::owned<Type> type = std::make_unique<Type>(
49 8 declaration.get_fully_qualified_name(),
50 16 Type::TYPE_COMPOSITE,
51 8 false,
52 16 declaration.get_name_source_ref());
53 8 mir.get_types().define_type(std::move(type));
54 8 }
55
56 Type *
57 336 TypeLowering::get_or_create(std::string pointer_name, Type::TypeType type_type, bool complete, const SourceReference & source_ref)
58 {
59 336 Type *pointer_type = mir.get_types().get_type(pointer_name);
60
2/2
✓ Branch 0 taken 214 times.
✓ Branch 1 taken 122 times.
336 if (pointer_type != nullptr) {
61 214 return pointer_type;
62 }
63 else {
64 122 Gyoji::owned<Type> pointer_type_created = std::make_unique<Type>(pointer_name, type_type, complete, source_ref);
65 122 pointer_type = pointer_type_created.get();
66 122 mir.get_types().define_type(std::move(pointer_type_created));
67 122 return pointer_type;
68 122 }
69 }
70
71 const Type*
72 2046 TypeLowering::extract_from_type_specifier_simple(const TypeSpecifierSimple & type_specifier)
73 {
74 2046 const auto & type_name = type_specifier.get_type_name();
75
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2046 times.
2046 if (type_name.is_expression()) {
76 auto error = std::make_unique<Gyoji::context::Error>("Could not resolve type");
77 error->add_message(type_name.get_name_source_ref(), "Specifying types from expressions is not yet supported.");
78 compiler_context.get_errors().add_error(std::move(error));
79 return nullptr;
80 }
81 2046 std::string name = type_name.get_name();
82 2046 const Type *type = mir.get_types().get_type(name);
83
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2046 times.
2046 if (type == nullptr) {
84 compiler_context
85 .get_errors()
86 .add_simple_error(type_name.get_name_source_ref(),
87 "Could not find type",
88 std::string("Could not resolve type ") + name
89 );
90 return nullptr;
91 }
92 2046 return type;
93 2046 }
94
95 const Type*
96 TypeLowering::extract_from_type_specifier_template(const TypeSpecifierTemplate & type_specifier)
97 {
98 compiler_context
99 .get_errors()
100 .add_simple_error(type_specifier.get_source_ref(),
101 "Could not find type",
102 "Template types are not supported yet."
103 );
104 return nullptr;
105 }
106
107 const Type*
108 4 TypeLowering::extract_from_type_specifier_function_pointer(const TypeSpecifierFunctionPointer & type_specifier)
109 {
110 4 const Type *return_type = extract_from_type_specifier(type_specifier.get_return_type());
111
112 4 std::vector<std::string> arg_list;
113 4 std::vector<Argument> fptr_arguments;
114
115
2/2
✓ Branch 7 taken 16 times.
✓ Branch 8 taken 4 times.
20 for (const auto & type_specifier : type_specifier.get_args().get_arguments()) {
116 16 const Type *argument_type = extract_from_type_specifier(*type_specifier);
117 16 Argument arg(argument_type, type_specifier->get_source_ref());
118 16 fptr_arguments.push_back(arg);
119 16 arg_list.push_back(argument_type->get_name());
120 16 }
121
122 // bool is_unsafe = method_unsafe_modifier.is_unsafe();
123 4 bool is_unsafe = false;
124 4 std::string arg_string = Gyoji::misc::join(arg_list, ",");
125
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
12 std::string unsafe_str = is_unsafe ? std::string("unsafe") : std::string("");
126 28 std::string pointer_name = return_type->get_name() + std::string("(") + unsafe_str + std::string("*)") + std::string("(") + arg_string + std::string(")");
127 4 Type *fptr_type = get_or_create(pointer_name, Type::TYPE_FUNCTION_POINTER, false, type_specifier.get_source_ref());
128
129
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (!fptr_type->is_complete()) {
130 fptr_type->complete_function_pointer_definition(
131 return_type,
132 fptr_arguments,
133 is_unsafe,
134 type_specifier.get_source_ref()
135 );
136 }
137
138 8 return fptr_type;
139 4 }
140
141 const Type*
142 128 TypeLowering::extract_from_type_specifier_pointer_to(const TypeSpecifierPointerTo & type_specifier)
143 {
144 128 const Type *pointer_target = extract_from_type_specifier(type_specifier.get_type_specifier());
145
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
128 if (pointer_target == nullptr) {
146 compiler_context
147 .get_errors()
148 .add_simple_error(type_specifier.get_source_ref(),
149 "Could not find type",
150 "Could not resolve target of pointer"
151 );
152 return nullptr;
153 }
154 128 const Type *pointer_type = mir.get_types().get_pointer_to(pointer_target, type_specifier.get_source_ref());
155 128 return pointer_type;
156 }
157
158 const Type*
159 8 TypeLowering::extract_from_type_specifier_reference_to(const TypeSpecifierReferenceTo & type_specifier)
160 {
161 8 const Type *pointer_target = extract_from_type_specifier(type_specifier.get_type_specifier());
162
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (pointer_target == nullptr) {
163 compiler_context
164 .get_errors()
165 .add_simple_error(type_specifier.get_source_ref(),
166 "Could not find type",
167 "Could not resolve target of reference"
168 );
169 return nullptr;
170 }
171 8 const Type *pointer_type = mir.get_types().get_reference_to(pointer_target, type_specifier.get_source_ref());
172 8 return pointer_type;
173 }
174
175 const Type*
176 TypeLowering::extract_from_type_specifier_array(const TypeSpecifierArray & type_specifier)
177 {
178 const Type *pointer_target = extract_from_type_specifier(type_specifier.get_type_specifier());
179 if (pointer_target == nullptr) {
180 compiler_context
181 .get_errors()
182 .add_simple_error(type_specifier.get_literal_int_token().get_source_ref(),
183 "Could not parse type of array elements.",
184 "Array element type could not be parsed."
185 );
186 return nullptr;
187 }
188
189 Gyoji::frontend::integers::ParseLiteralIntResult parse_result;
190 bool parsed = parse_literal_int(compiler_context, mir.get_types(), type_specifier.get_literal_int_token(), parse_result);
191 if (!parsed || parse_result.parsed_type == nullptr) {
192 compiler_context
193 .get_errors()
194 .add_simple_error(type_specifier.get_literal_int_token().get_source_ref(),
195 "Array size invalid",
196 "Could not parse array size."
197 );
198 return nullptr;
199 }
200 if (parse_result.parsed_type->get_type() != Type::TYPE_PRIMITIVE_u32) {
201 compiler_context
202 .get_errors()
203 .add_simple_error(type_specifier.get_literal_int_token().get_source_ref(),
204 "Array size invalid",
205 "Array size must be an unsigned 32-bit integer (u32) constant. Sizes may not be computed at runtime."
206 );
207 }
208
209 const Type *array_type = mir.get_types().get_array_of(pointer_target, parse_result.u32_value, type_specifier.get_source_ref());
210 return array_type;
211 }
212
213 const Type *
214 2186 TypeLowering::extract_from_type_specifier(const TypeSpecifier & type_specifier)
215 {
216 2186 const auto & type_specifier_type = type_specifier.get_type();
217
2/2
✓ Branch 1 taken 2046 times.
✓ Branch 2 taken 140 times.
2186 if (std::holds_alternative<Gyoji::owned<TypeSpecifierSimple>>(type_specifier_type)) {
218 2046 const auto & type_specifier = std::get<Gyoji::owned<TypeSpecifierSimple>>(type_specifier_type);
219 2046 return extract_from_type_specifier_simple(*type_specifier);
220 }
221
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 140 times.
140 else if (std::holds_alternative<Gyoji::owned<TypeSpecifierTemplate>>(type_specifier_type)) {
222 const auto & type_specifier = std::get<Gyoji::owned<TypeSpecifierTemplate>>(type_specifier_type);
223 return extract_from_type_specifier_template(*type_specifier);
224 }
225
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 136 times.
140 else if (std::holds_alternative<Gyoji::owned<TypeSpecifierFunctionPointer>>(type_specifier_type)) {
226 4 const auto & type_specifier = std::get<Gyoji::owned<TypeSpecifierFunctionPointer>>(type_specifier_type);
227 4 return extract_from_type_specifier_function_pointer(*type_specifier);
228 }
229
2/2
✓ Branch 1 taken 128 times.
✓ Branch 2 taken 8 times.
136 else if (std::holds_alternative<Gyoji::owned<TypeSpecifierPointerTo>>(type_specifier_type)) {
230 128 const auto & type_specifier = std::get<Gyoji::owned<TypeSpecifierPointerTo>>(type_specifier_type);
231 128 return extract_from_type_specifier_pointer_to(*type_specifier);
232 }
233
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 else if (std::holds_alternative<Gyoji::owned<TypeSpecifierReferenceTo>>(type_specifier_type)) {
234 8 const auto & type_specifier = std::get<Gyoji::owned<TypeSpecifierReferenceTo>>(type_specifier_type);
235 8 return extract_from_type_specifier_reference_to(*type_specifier);
236
237 }
238 else if (std::holds_alternative<Gyoji::owned<TypeSpecifierArray>>(type_specifier_type)) {
239 const auto & type_specifier = std::get<Gyoji::owned<TypeSpecifierArray>>(type_specifier_type);
240 return extract_from_type_specifier_array(*type_specifier);
241
242 }
243
244 compiler_context
245 .get_errors()
246 .add_simple_error(type_specifier.get_source_ref(),
247 "Compiler bug! Please report this message(2)",
248 "Unknown TypeSpecifier type in variant (compiler bug)"
249 );
250
251 return nullptr;
252 }
253
254 void
255 8 TypeLowering::extract_from_class_method_types(
256 Type &class_type,
257 std::map<std::string, TypeMethod> & methods,
258 std::string simple_name,
259 std::string fully_qualified_name,
260 Gyoji::mir::Symbol::SymbolType type,
261 const Gyoji::frontend::tree::UnsafeModifier & method_unsafe_modifier,
262 const Type *method_return_type,
263 const Gyoji::frontend::tree::FunctionDefinitionArgList & function_definition_arg_list,
264 const Gyoji::context::SourceReference & source_ref
265 )
266 {
267 8 std::vector<std::string> arg_list;
268 8 std::vector<Argument> fptr_arguments;
269 // First, pass the 'this' pointer
270 // to the function.
271
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 2 times.
8 if (type == Gyoji::mir::Symbol::SYMBOL_MEMBER_METHOD ||
272 type == Gyoji::mir::Symbol::SYMBOL_MEMBER_DESTRUCTOR) {
273 6 const Type * this_type = mir.get_types().get_pointer_to(&class_type, class_type.get_defined_source_ref());
274 6 Argument arg_this(this_type, source_ref);
275
276 6 fptr_arguments.push_back(arg_this);
277 6 arg_list.push_back(this_type->get_name());
278 6 }
279
280 const std::vector<Gyoji::owned<FunctionDefinitionArg>> & function_definition_args =
281 8 function_definition_arg_list.get_arguments();
282
2/2
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 8 times.
14 for (const auto & function_definition_arg : function_definition_args) {
283 6 const Type *argument_type = extract_from_type_specifier(function_definition_arg->get_type_specifier());
284 6 Argument arg(argument_type, function_definition_arg->get_source_ref());
285 6 fptr_arguments.push_back(arg);
286 6 arg_list.push_back(argument_type->get_name());
287 6 }
288
289 TypeMethod method(
290 simple_name,
291 source_ref,
292 &class_type,
293 method_return_type,
294 fptr_arguments
295 8 );
296
297 8 fprintf(stderr, "Defining method %s\n", simple_name.c_str());
298 8 methods.insert(std::pair(simple_name, method));
299
300 8 bool is_unsafe = method_unsafe_modifier.is_unsafe();
301 8 std::string arg_string = Gyoji::misc::join(arg_list, ",");
302
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
24 std::string unsafe_str = is_unsafe ? std::string("unsafe") : std::string("");
303 56 std::string pointer_name = method_return_type->get_name() + std::string("(") + unsafe_str + std::string("*)") + std::string("(") + arg_string + std::string(")");
304 8 Type *fptr_type = get_or_create(pointer_name, Type::TYPE_FUNCTION_POINTER, false, source_ref);
305
306
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 if (!fptr_type->is_complete()) {
307 8 fptr_type->complete_function_pointer_definition(
308 method_return_type,
309 fptr_arguments,
310 is_unsafe,
311 source_ref
312 );
313 }
314 8 fprintf(stderr, "Defining symbol %s\n", fully_qualified_name.c_str());
315
316 8 mir.get_symbols().define_symbol(
317 fully_qualified_name,
318 type,
319 fptr_type
320 );
321 8 }
322
323
324 void
325 18 TypeLowering::extract_from_class_members(Type & class_type, const ClassDefinition & class_definition)
326 {
327 18 std::vector<TypeMember> members;
328 18 std::map<std::string, const TypeMember*> members_by_name;
329 18 std::map<std::string, TypeMethod> methods;
330
331 18 const auto & class_members = class_definition.get_members();
332 18 size_t member_id = 0;
333
2/2
✓ Branch 5 taken 52 times.
✓ Branch 6 taken 18 times.
70 for (const auto & class_member : class_members) {
334 52 const auto & class_member_type = class_member->get_member();
335
2/2
✓ Branch 1 taken 44 times.
✓ Branch 2 taken 8 times.
52 if (std::holds_alternative<Gyoji::owned<ClassMemberDeclarationVariable>>(class_member_type)) {
336 44 const auto & member_variable = std::get<Gyoji::owned<ClassMemberDeclarationVariable>>(class_member_type);
337
338
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 44 times.
44 if (member_variable->get_unsafe_modifier().is_unsafe()) {
339 compiler_context
340 .get_errors()
341 .add_simple_error(member_variable->get_type_specifier().get_source_ref(),
342 "Member variables cannot be declared inherently unsafe.",
343 std::string("Member variable ") + member_variable->get_name() + std::string(" cannot be declared unsafe. This would not have any valid meaning.")
344 );
345 }
346 44 const Type *member_type = extract_from_type_specifier(member_variable->get_type_specifier());
347
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 if (member_type == nullptr) {
348 compiler_context
349 .get_errors()
350 .add_simple_error(member_variable->get_type_specifier().get_source_ref(),
351 "Could not find type",
352 std::string("Could not extract type of member variable ") + member_variable->get_name()
353 );
354 }
355 else {
356 44 const auto existing_member_it = members_by_name.find(member_variable->get_name());
357
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 44 times.
44 if (existing_member_it != members_by_name.end()) {
358 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Duplicate member variable in class.");
359 error->add_message(member_variable->get_name_source_ref(),
360 std::string("Member variable ") +
361 member_variable->get_name() +
362 std::string(" in class ") +
363 class_definition.get_name() +
364 std::string(" was already defined"));
365 error->add_message(existing_member_it->second->get_source_ref(),
366 "Originally defined here.");
367 compiler_context
368 .get_errors()
369 .add_error(std::move(error));
370 }
371 else {
372 44 TypeMember add_member(member_variable->get_name(), member_id, member_type, member_variable->get_name_source_ref());
373 44 members.push_back(add_member);
374 44 members_by_name.insert(std::pair(member_variable->get_name(), &members.back()));
375 44 member_id++;
376 44 }
377 }
378 }
379
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 6 times.
8 else if (std::holds_alternative<Gyoji::owned<ClassMemberDeclarationMethodStatic>>(class_member_type)) {
380 2 const auto & member_method = std::get<Gyoji::owned<ClassMemberDeclarationMethodStatic>>(class_member_type);
381 2 const Type * method_return_type = extract_from_type_specifier(member_method->get_type_specifier());
382
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (method_return_type == nullptr) {
383 fprintf(stderr, "Compiler bug! static method is missing a return type. This should not be possible at the semantics layer.\n");
384 exit(1);
385 }
386
387 2 std::string simple_name = member_method->get_identifier().get_name();
388 2 std::string fully_qualified_name = member_method->get_identifier().get_fully_qualified_name();
389
390 2 extract_from_class_method_types(
391 class_type,
392 methods,
393 simple_name,
394 fully_qualified_name,
395 Gyoji::mir::Symbol::SYMBOL_STATIC_FUNCTION,
396 member_method->get_unsafe_modifier(),
397 method_return_type,
398 member_method->get_arguments(),
399 2 member_method->get_source_ref()
400 );
401 2 }
402
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
6 else if (std::holds_alternative<Gyoji::owned<ClassMemberDeclarationMethod>>(class_member_type)) {
403 2 const auto & member_method = std::get<Gyoji::owned<ClassMemberDeclarationMethod>>(class_member_type);
404
405 // Regular methods have return types. Destructors always return void.
406 2 const Type * method_return_type = extract_from_type_specifier(member_method->get_type_specifier());
407
408 2 std::string simple_name = member_method->get_identifier().get_name();
409 2 std::string fully_qualified_name = member_method->get_identifier().get_fully_qualified_name();
410
411 2 extract_from_class_method_types(
412 class_type,
413 methods,
414 simple_name,
415 fully_qualified_name,
416 Gyoji::mir::Symbol::SYMBOL_MEMBER_METHOD,
417 member_method->get_unsafe_modifier(),
418 method_return_type,
419 member_method->get_arguments(),
420 2 member_method->get_source_ref()
421 );
422 2 }
423
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 else if (std::holds_alternative<Gyoji::owned<ClassMemberDeclarationDestructor>>(class_member_type)) {
424 4 const auto & member_method = std::get<Gyoji::owned<ClassMemberDeclarationDestructor>>(class_member_type);
425
426
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 if (member_method->get_arguments().get_arguments().size() != 0) {
427 compiler_context
428 .get_errors()
429 .add_simple_error(
430 member_method->get_source_ref(),
431 "Destructors may not have arguments",
432 std::string("Destructors may not have funciton arguments.")
433 );
434 continue;
435 }
436
437 // Regular methods have return types. Destructors always return void.
438 8 const Type * method_return_type = mir.get_types().get_type("void");
439
440 12 std::string simple_name = std::string("~") + class_type.get_simple_name();
441 4 std::string fully_qualified_name = class_type.get_name() + std::string("::") + simple_name;
442 4 fprintf(stderr, "Extracting destructor %s %s\n", simple_name.c_str(), fully_qualified_name.c_str());
443
444 4 extract_from_class_method_types(
445 class_type,
446 methods,
447 simple_name,
448 fully_qualified_name,
449 Gyoji::mir::Symbol::SYMBOL_MEMBER_DESTRUCTOR,
450 member_method->get_unsafe_modifier(),
451 method_return_type,
452 member_method->get_arguments(),
453 4 member_method->get_source_ref()
454 );
455 4 }
456 }
457
458 18 class_type.complete_composite_definition(
459 members,
460 methods,
461 class_definition.get_name_source_ref());
462 18 }
463
464 void
465 18 TypeLowering::extract_from_class_definition(const ClassDefinition & definition)
466 {
467 18 const auto it = mir.get_types().get_types().find(definition.get_fully_qualified_name());
468
469
2/2
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 6 times.
18 if (it == mir.get_types().get_types().end()) {
470 // Case 1: No forward declaration exists, fill in the definition
471 // from the class.
472 Gyoji::owned<Type> type = std::make_unique<Type>(
473 24 definition.get_fully_qualified_name(),
474 12 definition.get_name(),
475 24 Type::TYPE_COMPOSITE,
476 12 true,
477 24 definition.get_name_source_ref());
478 12 Type & class_type = *type.get();
479 12 mir.get_types().define_type(std::move(type));
480 12 extract_from_class_members(class_type, definition);
481 12 }
482 else {
483 6 auto & type = *it->second;
484 // Case 2: Class is forward declared, but is incomplete, so fill in the declaration.
485
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 if (!type.is_complete()) {
486 6 extract_from_class_members(type, definition);
487 }
488 else {
489 // Case 3: Class is declared and complete, but does not match our current definition,
490 // so this is a duplicate. Raise an error to avoid ambiguity.
491 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>(std::string("Duplicate class definition: ") + definition.get_fully_qualified_name());
492 error->add_message(type.get_defined_source_ref(),
493 "Originally defined here"
494 );
495 error->add_message(definition.get_name_source_ref(),
496 "Re-declared here"
497 );
498 compiler_context
499 .get_errors()
500 .add_error(std::move(error));
501 }
502
503 }
504 18 }
505
506 void
507 TypeLowering::extract_from_enum_definition(const EnumDefinition & enum_definition)
508 {
509 const auto it = mir.get_types().get_types().find(enum_definition.get_name());
510 if (it == mir.get_types().get_types().end()) {
511 // No definition exists, create it.
512 Gyoji::owned<Type> type = std::make_unique<Type>(enum_definition.get_name(), Type::TYPE_ENUM, true, enum_definition.get_name_source_ref());
513 mir.get_types().define_type(std::move(type));
514
515 for (const auto & ev : enum_definition.get_value_list().get_values()) {
516 fprintf(stderr, "Value is %s\n", ev->get_name().c_str());
517 }
518 // mir.get_symbols().define_symbol(
519 // fully_qualified_function_name,
520 // pointer_type
521 // );
522 }
523 else {
524 // This is a duplicate, reference the original definition.
525 // Case 3: Class is declared and complete, but does not match our current definition,
526 // so this is a duplicate. Raise an error to avoid ambiguity.
527 auto & type = *it->second;
528 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>(std::string("Duplicate enum definition: ") + enum_definition.get_name());
529 error->add_message(type.get_defined_source_ref(),
530 "Originally defined here"
531 );
532 error->add_message(enum_definition.get_name_source_ref(),
533 "Re-declared here"
534 );
535 compiler_context
536 .get_errors()
537 .add_error(std::move(error));
538 }
539 }
540
541 void
542 6 TypeLowering::extract_from_type_definition(const TypeDefinition & type_definition)
543 {
544 6 const auto it = mir.get_types().get_types().find(type_definition.get_name());
545
1/2
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 if (it == mir.get_types().get_types().end()) {
546 // No definition exists, create it.
547 6 const Type *defined_type = extract_from_type_specifier(type_definition.get_type_specifier());
548
549 // We effectively make a new type as a copy of the old one.
550 // This may seem redundant, but it's done
551 // so that we can later modify the type by "instantiating"
552 // it as a generic with specific type parameters
553 // when we get to that point.
554 Gyoji::owned<Type> type = std::make_unique<Type>(
555 6 type_definition.get_name(),
556 6 type_definition.get_name(),
557 6 type_definition.get_type_specifier().get_source_ref(),
558 *defined_type
559 12 );
560 6 mir.get_types().define_type(std::move(type));
561 6 }
562 else {
563 // This is a duplicate, reference the original definition.
564 // Case 3: Class is declared and complete, but does not match our current definition,
565 // so this is a duplicate. Raise an error to avoid ambiguity.
566 auto & type = *it->second;
567 std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>(std::string("Duplicate enum definition: ") + type_definition.get_name());
568 error->add_message(type.get_defined_source_ref(),
569 "Originally defined here"
570 );
571 error->add_message(type_definition.get_name_source_ref(),
572 "Re-declared here"
573 );
574 compiler_context
575 .get_errors()
576 .add_error(std::move(error));
577 }
578 6 }
579
580
581 void
582 318 TypeLowering::extract_from_function_specifications(
583 const Terminal & name,
584 Gyoji::mir::Symbol::SymbolType symbol_type,
585 const Type *return_type,
586 const SourceReference & return_type_source_ref,
587 const FunctionDefinitionArgList & function_argument_list,
588 const UnsafeModifier & unsafe_modifier
589 )
590 {
591
592 318 NS2Entity *entity = name.get_ns2_entity();
593 318 Type *maybe_class_type = mir.get_types().get_type(entity->get_parent()->get_fully_qualified_name());
594 318 const Type *class_pointer_type = nullptr;
595 318 bool is_method = false;
596
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 312 times.
318 if (maybe_class_type != nullptr) {
597 6 class_pointer_type = mir.get_types().get_pointer_to(maybe_class_type, return_type_source_ref);
598 6 is_method = true;
599 }
600
601 std::string fully_qualified_function_name =
602 318 name.get_fully_qualified_name();
603
604
605 //////
606 // Define the type of a function pointer.
607 //////
608 318 std::vector<std::string> arg_list;
609 318 std::vector<Argument> fptr_arguments;
610 318 const auto & function_definition_args = function_argument_list.get_arguments();
611
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 312 times.
318 if (is_method) {
612 6 arg_list.push_back(class_pointer_type->get_name());
613 6 fptr_arguments.push_back(
614 12 Argument(class_pointer_type, name.get_source_ref())
615 );
616 }
617
2/2
✓ Branch 5 taken 552 times.
✓ Branch 6 taken 318 times.
870 for (const auto & function_definition_arg : function_definition_args) {
618 552 std::string name = function_definition_arg->get_identifier().get_fully_qualified_name();
619 552 const Type * t = extract_from_type_specifier(function_definition_arg->get_type_specifier());
620 552 arg_list.push_back(t->get_name());
621 552 fptr_arguments.push_back(
622 1104 Argument(t, function_definition_arg->get_type_specifier().get_source_ref())
623 );
624 552 }
625 318 bool is_unsafe = unsafe_modifier.is_unsafe();
626 318 std::string arg_string = Gyoji::misc::join(arg_list, ",");
627
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 318 times.
✓ Branch 4 taken 318 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 318 times.
954 std::string unsafe_str = is_unsafe ? std::string("unsafe") : std::string("");
628 2226 std::string pointer_name = return_type->get_name() + std::string("(") + unsafe_str + std::string("*)") + std::string("(") + arg_string + std::string(")");
629 318 Type *pointer_type = get_or_create(pointer_name, Type::TYPE_FUNCTION_POINTER, false, name.get_source_ref());
630
631
2/2
✓ Branch 1 taken 110 times.
✓ Branch 2 taken 208 times.
318 if (!pointer_type->is_complete()) {
632 110 pointer_type->complete_function_pointer_definition(
633 return_type,
634 fptr_arguments,
635 is_unsafe,
636 name.get_source_ref()
637 );
638 }
639 //////
640 // Now that the type has been defined, we can
641 // define the symbol for this specific function.
642 //////
643 318 mir.get_symbols().define_symbol(
644 fully_qualified_function_name,
645 symbol_type,
646 pointer_type
647 );
648
649 318 }
650
651 void
652 270 TypeLowering::extract_from_function_definition(const FileStatementFunctionDefinition & function_definition)
653 {
654 const Type *return_type;
655 const SourceReference *return_type_source_ref;
656
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 266 times.
270 if (function_definition.is_destructor()) {
657 8 return_type = mir.get_types().get_type("void");
658 4 return_type_source_ref = &function_definition.get_name().get_source_ref();
659 }
660 else {
661 266 return_type = extract_from_type_specifier(function_definition.get_return_type());
662 266 return_type_source_ref = &function_definition.get_return_type().get_source_ref();
663
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 266 times.
266 if (return_type == nullptr) {
664 compiler_context
665 .get_errors()
666 .add_simple_error(function_definition.get_return_type().get_source_ref(),
667 "Compiler bug! Please report this message(3)",
668 "Function pointer type declared with invalid type"
669 );
670 return;
671 }
672 }
673 270 extract_from_function_specifications(
674 function_definition.get_name(),
675 Gyoji::mir::Symbol::SYMBOL_STATIC_FUNCTION,
676 return_type,
677 *return_type_source_ref,
678 function_definition.get_arguments(),
679 function_definition.get_unsafe_modifier()
680 );
681 }
682
683 void
684 48 TypeLowering::extract_from_function_declaration(const FileStatementFunctionDeclaration & function_declaration)
685 {
686 const Type *return_type;
687 const SourceReference *return_type_source_ref;
688
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 48 times.
48 if (function_declaration.is_destructor()) {
689 return_type = mir.get_types().get_type("void");
690 return_type_source_ref = &function_declaration.get_name().get_source_ref();
691 }
692 else {
693 48 return_type = extract_from_type_specifier(function_declaration.get_return_type());
694 48 return_type_source_ref = &function_declaration.get_return_type().get_source_ref();
695
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 if (return_type == nullptr) {
696 compiler_context
697 .get_errors()
698 .add_simple_error(function_declaration.get_return_type().get_source_ref(),
699 "Compiler bug! Please report this message(3)",
700 "Function pointer type declared with invalid type"
701 );
702 return;
703 }
704 }
705 48 extract_from_function_specifications(
706 function_declaration.get_name(),
707 Gyoji::mir::Symbol::SYMBOL_STATIC_FUNCTION,
708 return_type,
709 *return_type_source_ref,
710 function_declaration.get_arguments(),
711 function_declaration.get_unsafe_modifier()
712 );
713 }
714
715 void
716 12 TypeLowering::extract_from_namespace(const FileStatementNamespace & namespace_declaration)
717 {
718 12 const auto & statements = namespace_declaration.get_statement_list().get_statements();
719 12 extract_types(statements);
720 12 }
721
722 void
723 42 TypeLowering::extract_types(const std::vector<Gyoji::owned<FileStatement>> & statements)
724 {
725
2/2
✓ Branch 5 taken 368 times.
✓ Branch 6 taken 42 times.
410 for (const auto & statement : statements) {
726 368 const auto & file_statement = statement->get_statement();
727
2/2
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 320 times.
368 if (std::holds_alternative<Gyoji::owned<FileStatementFunctionDeclaration>>(file_statement)) {
728 48 extract_from_function_declaration(*std::get<Gyoji::owned<FileStatementFunctionDeclaration>>(file_statement));
729 }
730
2/2
✓ Branch 1 taken 270 times.
✓ Branch 2 taken 50 times.
320 else if (std::holds_alternative<Gyoji::owned<FileStatementFunctionDefinition>>(file_statement)) {
731 270 extract_from_function_definition(*std::get<Gyoji::owned<FileStatementFunctionDefinition>>(file_statement));
732 }
733
2/2
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 2 times.
50 else if (std::holds_alternative<Gyoji::owned<FileStatementGlobalDefinition>>(file_statement)) {
734 // TODO: We should extract the global symbols
735 // from here.
736 }
737
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 40 times.
48 else if (std::holds_alternative<Gyoji::owned<ClassDeclaration>>(file_statement)) {
738 8 extract_from_class_declaration(*std::get<Gyoji::owned<ClassDeclaration>>(file_statement));
739 }
740
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 22 times.
40 else if (std::holds_alternative<Gyoji::owned<ClassDefinition>>(file_statement)) {
741 18 extract_from_class_definition(*std::get<Gyoji::owned<ClassDefinition>>(file_statement));
742 }
743
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
22 else if (std::holds_alternative<Gyoji::owned<EnumDefinition>>(file_statement)) {
744 extract_from_enum_definition(*std::get<Gyoji::owned<EnumDefinition>>(file_statement));
745 }
746
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 16 times.
22 else if (std::holds_alternative<Gyoji::owned<TypeDefinition>>(file_statement)) {
747 6 extract_from_type_definition(*std::get<Gyoji::owned<TypeDefinition>>(file_statement));
748 }
749
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 4 times.
16 else if (std::holds_alternative<Gyoji::owned<FileStatementNamespace>>(file_statement)) {
750 12 extract_from_namespace(*std::get<Gyoji::owned<FileStatementNamespace>>(file_statement));
751 }
752
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 else if (std::holds_alternative<Gyoji::owned<FileStatementUsing>>(file_statement)) {
753 // Nothing, no statements can be declared inside here.
754 }
755 else {
756 compiler_context
757 .get_errors()
758 .add_simple_error(statement->get_source_ref(),
759 "Compiler bug! Please report this message(1)",
760 "Unknown statement type in variant, extracting statements from file (compiler bug)"
761 );
762 }
763 }
764 42 }
765
766