Forward declared type
Some checks failed
CI / ci (MinSizeRel, ubuntu-latest) (push) Failing after 34s
CI / ci (RelWithDebInfo, ubuntu-latest) (push) Failing after 34s
CI / ci (Release, ubuntu-latest) (push) Failing after 32s
CI / ci (Debug, ubuntu-latest) (push) Failing after 1m14s

This commit is contained in:
David Gonzalez Martin 2025-05-23 20:02:10 -06:00
parent 5de4ba76f5
commit e74d3fe127
9 changed files with 1920 additions and 473 deletions

View File

@ -48,5 +48,3 @@ target_link_libraries(bb PUBLIC
) )
target_compile_options(bb PRIVATE -Wall -Wextra -pedantic -Wpedantic -Werror -Wno-c99-extensions -Wno-unused-function -Wno-missing-designated-field-initializers -funsigned-char -fwrapv -fno-strict-aliasing) target_compile_options(bb PRIVATE -Wall -Wextra -pedantic -Wpedantic -Werror -Wno-c99-extensions -Wno-unused-function -Wno-missing-designated-field-initializers -funsigned-char -fwrapv -fno-strict-aliasing)
# target_compile_options(bb PRIVATE -fsanitize=address)
# target_link_options(bb PRIVATE -fsanitize=address)

File diff suppressed because it is too large Load Diff

View File

@ -306,8 +306,12 @@ global_variable String names[] =
string_literal("noreturn_macro"), string_literal("noreturn_macro"),
string_literal("generic_pointer_array"), string_literal("generic_pointer_array"),
string_literal("self_referential_struct"), // TODO string_literal("self_referential_struct"),
// string_literal("forward_declared_type"), string_literal("forward_declared_type"),
string_literal("enum_array"),
string_literal("opaque"),
string_literal("basic_struct_passing"),
}; };
void entry_point(Slice<const char*> arguments, Slice<char* const> environment) void entry_point(Slice<const char*> arguments, Slice<char* const> environment)
@ -382,7 +386,7 @@ void entry_point(Slice<const char*> arguments, Slice<char* const> environment)
if (arguments.length >= 5) if (arguments.length >= 5)
{ {
auto has_debug_info_string = c_string_to_slice(arguments[3]); auto has_debug_info_string = c_string_to_slice(arguments[4]);
if (has_debug_info_string.equal(string_literal("true"))) if (has_debug_info_string.equal(string_literal("true")))
{ {
has_debug_info = true; has_debug_info = true;
@ -488,7 +492,7 @@ void entry_point(Slice<const char*> arguments, Slice<char* const> environment)
auto success = execution.termination_kind == TerminationKind::exit && execution.termination_code == 0; auto success = execution.termination_kind == TerminationKind::exit && execution.termination_code == 0;
if (!success) if (!success)
{ {
print(string_literal("Standalone test failed: ")); print(string_literal("Self-hosted test failed: "));
print(name); print(name);
print(string_literal("\n")); print(string_literal("\n"));
bb_fail(); bb_fail();

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <lib.hpp> #include <lib.hpp>
#include <llvm-c/Types.h> #include <llvm-c/TargetMachine.h>
#define report_error() trap() #define report_error() trap()
enum class Command enum class Command
@ -314,6 +314,8 @@ enum class TypeId
unresolved, unresolved,
vector, vector,
floating_point, floating_point,
enum_array,
opaque,
}; };
struct TypeInteger struct TypeInteger
@ -448,6 +450,13 @@ struct LLVMType
LLVMMetadataRef debug; LLVMMetadataRef debug;
}; };
struct TypeEnumArray
{
Type* enum_type;
Type* element_type;
Type* next;
};
struct Type struct Type
{ {
union union
@ -461,6 +470,7 @@ struct Type
TypeBits bits; TypeBits bits;
TypeAlias alias; TypeAlias alias;
TypeUnion union_type; TypeUnion union_type;
TypeEnumArray enum_array;
}; };
TypeId id; TypeId id;
String name; String name;
@ -528,6 +538,17 @@ fn u64 get_byte_size(Type* type)
auto result = type->union_type.byte_size; auto result = type->union_type.byte_size;
return result; return result;
} break; } break;
case TypeId::enum_array:
{
auto enum_type = type->enum_array.enum_type;
assert(enum_type->id == TypeId::enumerator);
auto element_count = enum_type->enumerator.fields.length;
auto element_type = type->enum_array.element_type;
auto element_size = get_byte_size(element_type);
auto result = element_size * element_count;
return result;
} break;
default: trap(); default: trap();
} }
} }
@ -575,6 +596,10 @@ fn u32 get_byte_alignment(Type* type)
{ {
return get_byte_alignment(type->alias.type); return get_byte_alignment(type->alias.type);
} break; } break;
case TypeId::enum_array:
{
return get_byte_alignment(type->enum_array.element_type);
} break;
default: trap(); default: trap();
} }
} }
@ -609,6 +634,13 @@ struct Scope
LLVMMetadataRef llvm; LLVMMetadataRef llvm;
}; };
struct SourceLocation
{
Scope* scope;
u32 line;
u32 column;
};
enum class StatementId enum class StatementId
{ {
local, local,
@ -878,10 +910,18 @@ struct ValueVaArg
Type* type; Type* type;
}; };
struct AggregateInitializationElement
{
String name;
Value* value;
u32 line;
u32 column;
};
struct ValueAggregateInitialization struct ValueAggregateInitialization
{ {
Slice<String> names; Slice<AggregateInitializationElement> elements;
Slice<Value*> values; Scope* scope;
bool is_constant; bool is_constant;
bool zero; bool zero;
}; };
@ -987,6 +1027,7 @@ struct Value
case ValueId::enum_literal: case ValueId::enum_literal:
case ValueId::unary_type: case ValueId::unary_type:
case ValueId::string_literal: case ValueId::string_literal:
case ValueId::zero:
return true; return true;
case ValueId::unary: case ValueId::unary:
case ValueId::binary: case ValueId::binary:
@ -1103,6 +1144,8 @@ struct ModuleLLVM
LLVMBuilderRef builder; LLVMBuilderRef builder;
LLVMDIBuilderRef di_builder; LLVMDIBuilderRef di_builder;
LLVMMetadataRef file; LLVMMetadataRef file;
LLVMTargetMachineRef target_machine;
LLVMTargetDataRef target_data;
LLVMMetadataRef compile_unit; LLVMMetadataRef compile_unit;
LLVMTypeRef pointer_type; LLVMTypeRef pointer_type;
LLVMTypeRef void_type; LLVMTypeRef void_type;
@ -1126,6 +1169,7 @@ struct Module
Type* first_slice_type; Type* first_slice_type;
Type* first_pair_struct_type; Type* first_pair_struct_type;
Type* first_array_type; Type* first_array_type;
Type* first_enum_array_type;
Type* first_type; Type* first_type;
Type* last_type; Type* last_type;
@ -1714,5 +1758,52 @@ fn Local* new_local(Module* module, Scope* scope)
return result; return result;
} }
fn Type* get_enum_array_type(Module* module, Type* enum_type, Type* element_type)
{
assert(enum_type);
assert(element_type);
Type* last_enum_type = module->first_enum_array_type;
if (last_enum_type)
{
while (1)
{
assert(last_enum_type->id == TypeId::enum_array);
if (last_enum_type->enum_array.enum_type == enum_type && last_enum_type->enum_array.element_type == element_type)
{
return last_enum_type;
}
if (!last_enum_type->enum_array.next)
{
break;
}
last_enum_type = last_enum_type->enum_array.next;
}
}
String name_parts[] = {
string_literal("enum_array["),
enum_type->name,
string_literal("]("),
element_type->name,
string_literal(")"),
};
auto enum_array_type = type_allocate_init(module, {
.enum_array = {
.enum_type = enum_type,
.element_type = element_type,
},
.id = TypeId::enum_array,
.name = arena_join_string(module->arena, array_to_slice(name_parts)),
});
return enum_array_type;
}
void parse(Module* module); void parse(Module* module);
void emit(Module* module); void emit(Module* module);

File diff suppressed because it is too large Load Diff

View File

@ -450,7 +450,6 @@ fn u64 parse_integer_decimal_assume_valid(String string)
fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder); fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder);
fn Type* parse_type(Module* module, Scope* scope) fn Type* parse_type(Module* module, Scope* scope)
{ {
auto start_character = module->content[module->offset]; auto start_character = module->content[module->offset];
@ -465,6 +464,20 @@ fn Type* parse_type(Module* module, Scope* scope)
{ {
return noreturn_type(module); return noreturn_type(module);
} }
else if (identifier.equal(string_literal("enum_array")))
{
skip_space(module);
expect_character(module, left_bracket);
auto enum_type = parse_type(module, scope);
expect_character(module, right_bracket);
expect_character(module, left_parenthesis);
auto element_type = parse_type(module, scope);
expect_character(module, right_parenthesis);
auto enum_array_type = get_enum_array_type(module, enum_type, element_type);
return enum_array_type;
}
else else
{ {
auto is_int_type = identifier.length > 1 && (identifier[0] == 's' || identifier[0] == 'u'); auto is_int_type = identifier.length > 1 && (identifier[0] == 's' || identifier[0] == 'u');
@ -1191,6 +1204,89 @@ fn Token tokenize(Module* module)
fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder); fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder);
fn Value* parse_aggregate_initialization(Module* module, Scope* scope, ValueBuilder builder, u8 end_ch)
{
skip_space(module);
u64 field_count = 0;
AggregateInitializationElement element_buffer[64];
bool zero = false;
while (1)
{
skip_space(module);
if (consume_character_if_match(module, end_ch))
{
break;
}
auto field_index = field_count;
if (consume_character_if_match(module, '.'))
{
auto name = parse_identifier(module);
skip_space(module);
expect_character(module, '=');
skip_space(module);
auto line = get_line(module);
auto column = get_column(module);
auto value = parse_value(module, scope, {});
skip_space(module);
consume_character_if_match(module, ',');
element_buffer[field_index] = {
.name = name,
.value = value,
.line = line,
.column = column,
};
}
else
{
auto token = tokenize(module);
zero = token.id == TokenId::value_keyword && token.value_keyword == ValueKeyword::zero;
if (zero)
{
skip_space(module);
if (consume_character_if_match(module, ','))
{
skip_space(module);
}
expect_character(module, right_brace);
break;
}
else
{
report_error();
}
}
field_count += 1;
}
auto elements = arena_allocate<AggregateInitializationElement>(module->arena, field_count);
memcpy(elements.pointer, element_buffer, sizeof(element_buffer[0]) * field_count);
auto result = new_value(module);
*result = {
.aggregate_initialization = {
.elements = elements,
.scope = scope,
.is_constant = false,
.zero = zero,
},
.id = ValueId::aggregate_initialization,
.kind = builder.kind,
};
return result;
}
fn Value* parse_precedence(Module* module, Scope* scope, ValueBuilder builder); fn Value* parse_precedence(Module* module, Scope* scope, ValueBuilder builder);
fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
{ {
@ -1422,33 +1518,42 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
u64 element_count = 0; u64 element_count = 0;
Value* value_buffer[64]; Value* value_buffer[64];
while (1) skip_space(module);
{
skip_space(module);
if (consume_character_if_match(module, right_bracket)) if (module->content[module->offset] == '.')
{
result = parse_aggregate_initialization(module, scope, builder, right_bracket);
}
else
{
while (1)
{ {
break; skip_space(module);
if (consume_character_if_match(module, right_bracket))
{
break;
}
auto value = parse_value(module, scope, {});
value_buffer[element_count] = value;
element_count += 1;
consume_character_if_match(module, ',');
} }
auto value = parse_value(module, scope, {}); auto values = new_value_array(module, element_count);
value_buffer[element_count] = value; memcpy(values.pointer, value_buffer, element_count * sizeof(Value*));
element_count += 1;
consume_character_if_match(module, ','); result = new_value(module);
*result = {
.array_initialization = {
.values = values,
.is_constant = false, // This is analyzed later
},
.id = ValueId::array_initialization,
};
} }
auto values = new_value_array(module, element_count);
memcpy(values.pointer, value_buffer, element_count * sizeof(Value*));
result = new_value(module);
*result = {
.array_initialization = {
.values = values,
.is_constant = false, // This is analyzed later
},
.id = ValueId::array_initialization,
};
} break; } break;
case TokenId::dot: case TokenId::dot:
{ {
@ -1477,76 +1582,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
} break; } break;
case TokenId::left_brace: case TokenId::left_brace:
{ {
skip_space(module); result = parse_aggregate_initialization(module, scope, builder, right_brace);
u64 field_count = 0;
String name_buffer[64];
Value* value_buffer[64];
bool zero = false;
while (1)
{
skip_space(module);
if (consume_character_if_match(module, right_brace))
{
break;
}
auto field_index = field_count;
if (consume_character_if_match(module, '.'))
{
auto name = parse_identifier(module);
name_buffer[field_index] = name;
skip_space(module);
expect_character(module, '=');
skip_space(module);
auto value = parse_value(module, scope, {});
value_buffer[field_index] = value;
skip_space(module);
consume_character_if_match(module, ',');
}
else
{
auto token = tokenize(module);
zero = token.id == TokenId::value_keyword && token.value_keyword == ValueKeyword::zero;
if (zero)
{
skip_space(module);
if (consume_character_if_match(module, ','))
{
skip_space(module);
}
expect_character(module, right_brace);
break;
}
else
{
report_error();
}
}
field_count += 1;
}
auto names = arena_allocate<String>(module->arena, field_count);
memcpy(names.pointer, name_buffer, sizeof(String) * field_count);
auto values = new_value_array(module, field_count);
memcpy(values.pointer, value_buffer, sizeof(Value*) * field_count);
result = new_value(module);
*result = {
.aggregate_initialization = {
.names = names,
.values = values,
.is_constant = false,
.zero = zero,
},
.id = ValueId::aggregate_initialization,
};
} break; } break;
case TokenId::value_keyword: case TokenId::value_keyword:
{ {
@ -2779,6 +2815,7 @@ void parse(Module* module)
enumerator, enumerator,
function, function,
macro, macro,
opaque,
structure, structure,
typealias, typealias,
union_type, union_type,
@ -2798,6 +2835,7 @@ void parse(Module* module)
string_literal("enum"), string_literal("enum"),
string_literal("fn"), string_literal("fn"),
string_literal("macro"), string_literal("macro"),
string_literal("opaque"),
string_literal("struct"), string_literal("struct"),
string_literal("typealias"), string_literal("typealias"),
string_literal("union"), string_literal("union"),
@ -3485,6 +3523,9 @@ void parse(Module* module)
field_count += 1; field_count += 1;
} }
byte_size = align_forward(byte_size, byte_alignment);
assert(byte_size % byte_alignment == 0);
skip_space(module); skip_space(module);
consume_character_if_match(module, ';'); consume_character_if_match(module, ';');
@ -3605,6 +3646,16 @@ void parse(Module* module)
}; };
union_type->id = TypeId::union_type; union_type->id = TypeId::union_type;
} break; } break;
case GlobalKeyword::opaque:
{
skip_space(module);
expect_character(module, ';');
auto opaque_type = type_allocate_init(module, {
.id = TypeId::opaque,
.name = global_name,
});
unused(opaque_type);
} break;
case GlobalKeyword::count: case GlobalKeyword::count:
{ {
set_checkpoint(module, checkpoint); set_checkpoint(module, checkpoint);

View File

@ -0,0 +1,140 @@
CallingConvention = enum
{
c,
}
InlineBehavior = enum
{
default = 0,
always_inline = 1,
no_inline = 2,
inline_hint = 3,
}
FunctionAttributes = struct
{
inline_behavior: InlineBehavior,
naked: u1,
}
Type = struct;
TypeId = enum
{
void,
noreturn,
forward_declaration,
integer,
function,
pointer,
array,
enum,
struct,
bits,
alias,
union,
unresolved,
vector,
floating_point,
enum_array,
opaque,
}
TypeInteger = struct
{
bit_count: u32,
signed: u1,
}
TypePointer = struct
{
element_type: &Type,
next: &Type,
}
AbiRegisterCountSystemV = struct
{
gpr: u32,
sse: u32,
};
AbiRegisterCount = union
{
system_v: AbiRegisterCountSystemV,
};
AbiInformation = struct
{
foo: u32,
}
TypeFunction = struct
{
semantic_return_type: &Type,
semantic_argument_types: []&Type,
calling_convention: CallingConvention,
is_variable_arguments: u1,
abi_argument_types: []&Type,
abi_return_type: &Type,
available_registers: AbiRegisterCount,
argument_abis: []AbiInformation,
return_abi: AbiInformation,
}
TypeContent = union
{
integer: TypeInteger,
function: TypeFunction,
pointer: TypePointer,
}
Type = struct
{
content: TypeContent,
id: TypeId,
name: []u8,
next: &Type,
}
give_me_string = fn () []u8
{
return "foooo";
}
get_type = fn (src: &Type, type: Type) &Type
{
src.& = type;
return src;
}
require = fn (ok: u1) void
{
if (!ok) #trap();
}
[export] main = fn [cc(c)] () s32
{
>buffer: Type = undefined;
>pointer = &buffer;
require(pointer == &buffer);
require(pointer != zero);
>result = get_type(pointer, {
.content = {
.pointer = {
.element_type = pointer,
zero,
},
},
.id = .pointer,
.name = give_me_string(),
zero,
});
require(pointer != zero);
require(pointer == &buffer);
require(buffer.content.pointer.element_type == pointer);
require(buffer.id == .pointer);
return 0;
}

22
tests/enum_array.bbb Normal file
View File

@ -0,0 +1,22 @@
require = fn (ok: u1) void
{
if (!ok) #trap();
}
E = enum
{
a,
b,
c,
d,
}
[export] main = fn [cc(c)] () s32
{
>some_enum_array: enum_array[E](u32) = [ .a = 4, .b = 3, .c = 2, .d = 1 ];
require(some_enum_array[.a] == 4);
require(some_enum_array[.b] == 3);
require(some_enum_array[.c] == 2);
require(some_enum_array[.d] == 1);
return 0;
}

16
tests/opaque.bbb Normal file
View File

@ -0,0 +1,16 @@
OpaqueType = opaque;
[extern] memcpy = fn [cc(c)] (destination: &s32, source: &s32, size: u64) &OpaqueType;
[export] main = fn [cc(c)] () s32
{
>destination: s32 = 1;
>source: s32 = 0;
>opaque_pointer = memcpy(&destination, &source, #byte_size(s32));
>pointer: &s32 = #pointer_cast(opaque_pointer);
if (pointer != &destination)
{
#trap();
}
return destination;
}