Implement for, among other stuff
All checks were successful
CI / ci (Release, ubuntu-latest) (pull_request) Successful in 3m25s
CI / ci (RelWithDebInfo, ubuntu-latest) (pull_request) Successful in 3m27s
CI / ci (MinSizeRel, ubuntu-latest) (pull_request) Successful in 3m30s
CI / ci (Debug, ubuntu-latest) (pull_request) Successful in 13m48s
CI / ci (MinSizeRel, ubuntu-latest) (push) Successful in 3m31s
CI / ci (RelWithDebInfo, ubuntu-latest) (push) Successful in 3m28s
CI / ci (Release, ubuntu-latest) (push) Successful in 3m26s
CI / ci (Debug, ubuntu-latest) (push) Successful in 13m48s

This commit is contained in:
David Gonzalez Martin 2025-06-14 20:34:37 -06:00
parent b24b5f29f7
commit adfce1d43e
2 changed files with 829 additions and 33 deletions

View File

@ -2031,6 +2031,7 @@ value_is_constant = fn (value: &Value) u1
{
.constant_integer,
.enum_literal,
.string_literal,
=>
{
return 1;
@ -2782,8 +2783,8 @@ StatementFor = struct
{
first_local: &Local,
last_local: &Local,
kinds: ValueKind,
iteratables: &Value,
kinds: []ValueKind,
iteratables: []&Value,
predicate: &Statement,
scope: Scope,
kind: StatementForKind,
@ -2811,6 +2812,12 @@ Statement = struct
id: StatementId,
}
scope_to_for = fn (scope: &Scope) &StatementFor
{
assert(scope.kind == .for_each);
return #field_parent_pointer(scope, "scope");
}
scope_to_block = fn (scope: &Scope) &Block
{
assert(scope.kind == .local);
@ -2852,7 +2859,17 @@ new_local = fn (module: &Module, scope: &Scope) &Local
},
.for_each =>
{
#trap();
>for_each = scope_to_for(scope);
if (for_each.last_local)
{
for_each.last_local.next = result;
}
else
{
for_each.first_local = result;
}
for_each.last_local = result;
}
else =>
{
@ -4918,7 +4935,20 @@ reference_identifier = fn (module: &Module, current_scope: &Scope, identifier: [
},
.for_each =>
{
#trap();
assert(scope.parent != zero);
>for_each = scope_to_for(scope);
>local = for_each.first_local;
while (local)
{
if (string_equal(identifier, local.variable.name))
{
variable = &local.variable;
break;
}
local = local.next;
}
},
.macro_declaration =>
{
@ -6030,7 +6060,137 @@ parse_statement = fn (module: &Module, scope: &Scope) &Statement
},
.for =>
{
#trap();
skip_space(module);
expect_character(module, left_parenthesis);
skip_space(module);
>parent_scope = scope;
statement.content = {
.for = {
.scope = {
.parent = parent_scope,
.line = statement_line,
.column = statement_column,
.kind = .for_each,
zero,
},
zero,
},
};
statement.id = .for;
>scope = &statement.content.for.scope;
>left_value_buffer: [64]ValueKind = undefined;
>left_value_count: u64 = 0;
while (1)
{
skip_space(module);
>is_left = module.content[module.offset] == '&';
module.offset += #extend(is_left);
>for_local_line = get_line(module);
>for_local_column = get_column(module);
if (is_identifier_start(module.content[module.offset]))
{
>local_name = arena_duplicate_string(module.arena, parse_identifier(module));
>local = new_local(module, scope);
local.& = {
.variable = {
.scope = scope,
.name = local_name,
.line = for_local_line,
.column = for_local_column,
zero,
},
zero,
};
>kind: ValueKind = #select(is_left, .left, .right);
left_value_buffer[left_value_count] = kind;
left_value_count += 1;
}
else
{
#trap();
}
skip_space(module);
if (!consume_character_if_match(module, ','))
{
expect_character(module, ':');
break;
}
}
skip_space(module);
>right_value_buffer: [64]&Value = undefined;
>right_value_count: u64 = 0;
skip_space(module);
right_value_buffer[right_value_count] = parse_value(module, scope, zero);
right_value_count += 1;
skip_space(module);
>token = tokenize(module);
>for_kind: StatementForKind = undefined;
switch (token.id)
{
.double_dot =>
{
if (left_value_count != 1)
{
report_error();
}
right_value_buffer[right_value_count] = parse_value(module, scope, zero);
right_value_count += 1;
expect_character(module, right_parenthesis);
for_kind = .range;
},
.right_parenthesis =>
{
right_value_buffer[0].kind = .left;
for_kind = .slice;
},
else => { report_error(); },
}
statement.content.for.kind = for_kind;
if (for_kind == .slice and left_value_count != right_value_count)
{
report_error();
}
>left_values = arena_allocate_slice[ValueKind](module.arena, left_value_count);
memcpy(#pointer_cast(left_values.pointer), #pointer_cast(&left_value_buffer), left_value_count * #byte_size(ValueKind));
>right_values = arena_allocate_slice[&Value](module.arena, right_value_count);
memcpy(#pointer_cast(right_values.pointer), #pointer_cast(&right_value_buffer), right_value_count * #byte_size(&Value));
statement.content.for.kinds = left_values;
statement.content.for.iteratables = right_values;
skip_space(module);
>predicate = parse_statement(module, scope);
statement.content.for.predicate = predicate;
skip_space(module);
require_semicolon = 0;
},
.while =>
{
@ -6680,7 +6840,7 @@ parse = fn (module: &Module) void
for (i: 0..field_count)
{
>value = int_value_buffer[i];
>highest_value = #max(highest_value, value);
highest_value = #max(highest_value, value);
fields[i] = {
.name = name_buffer[i],
.value = value,
@ -8938,7 +9098,120 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi
},
.enum_name =>
{
#trap();
>string_type = get_slice_type(module, uint8(module));
typecheck(module, expected_type, string_type);
analyze_type(module, unary_value, zero, { .must_be_constant = analysis.must_be_constant, zero });
>enum_type = unary_value.type;
if (enum_type.id != .enum)
{
report_error();
}
resolve_type_in_place(module, enum_type);
>enum_to_string_function = enum_type.content.enum.enum_to_string_function;
if (!enum_to_string_function)
{
>current_block = LLVMGetInsertBlock(module.llvm.builder);
>enum_name_array_global = get_enum_name_array_global(module, enum_type);
>argument_types: [_]&LLVMType = [ enum_type.llvm.abi ];
>llvm_function_type = LLVMFunctionType(string_type.llvm.memory, &argument_types[0], argument_types.length, 0);
>function_name = arena_join_string(module.arena, [ "enum_to_string.", enum_type.name ][..]);
>llvm_function = llvm_module_create_function(module.llvm.module, llvm_function_type, .internal, function_name);
LLVMSetFunctionCallConv(llvm_function, .fast);
>llvm_argument: &LLVMValue = zero;
//assert(argument_types.length == 1); TODO: constant evaluation
LLVMGetParams(llvm_function, &llvm_argument);
>entry_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "entry");
LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block);
>u32_type = uint32(module);
resolve_type_in_place(module, u32_type);
// TODO: alloca insert point
>alloca = create_alloca(module, {
.type = string_type,
.name = "return_value",
zero,
});
>return_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "return_block");
>else_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "else_block");
>enum_fields = enum_type.content.enum.fields;
>switch_instruction = LLVMBuildSwitch(module.llvm.builder, llvm_argument, else_block, #truncate(enum_fields.length));
>backing_type = enum_type.llvm.abi;
assert(backing_type != zero);
>u64_type = uint64(module).llvm.abi;
assert(u64_type != zero);
for (i: 0..enum_fields.length)
{
>field = &enum_fields[i];
>case_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "case_block");
>case_value = LLVMConstInt(backing_type, field.value, 0);
LLVMAddCase(switch_instruction, case_value, case_block);
LLVMPositionBuilderAtEnd(module.llvm.builder, case_block);
>case_value_result_pointer = create_gep(module, {
.type = enum_name_array_global.variable.type.llvm.memory,
.pointer = enum_name_array_global.variable.storage.llvm,
.indices = [ LLVMConstNull(u64_type), LLVMConstInt(u64_type, i, 0) ][..],
zero,
});
>case_value_result = create_load(module, {
.type = string_type,
.pointer = case_value_result_pointer,
zero,
});
create_store(module, {
.source = case_value_result,
.destination = alloca,
.type = string_type,
zero,
});
LLVMBuildBr(module.llvm.builder, return_block);
}
LLVMPositionBuilderAtEnd(module.llvm.builder, else_block);
LLVMBuildUnreachable(module.llvm.builder);
LLVMPositionBuilderAtEnd(module.llvm.builder, return_block);
>function_result = create_load(module, {
.type = string_type,
.pointer = alloca,
zero,
});
LLVMBuildRet(module.llvm.builder, function_result);
if (current_block)
{
LLVMPositionBuilderAtEnd(module.llvm.builder, current_block);
}
enum_to_string_function = llvm_function;
enum_type.content.enum.enum_to_string_function = enum_to_string_function;
// TODO: alloca insertion point restoration
}
assert(enum_to_string_function != zero);
value_type = string_type;
},
.pointer_from_int =>
{
@ -9365,7 +9638,39 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi
},
.enum_array, .array =>
{
#trap();
if (!string_equal(field_name, "length"))
{
report_error();
}
if (expected_type)
{
if (expected_type.id != .integer)
{
report_error();
}
value_type = expected_type;
}
else
{
if (resolved_aggregate_type.id == .enum_array)
{
>enum_type = resolved_aggregate_type.content.enum_array.enum_type;
>backing_type = enum_type.content.enum.backing_type;
value_type = backing_type;
}
else if (resolved_aggregate_type.id == .array)
{
value_type = uint64(module);
}
else
{
unreachable;
}
}
// TODO: see if it fits
},
.pointer =>
{
@ -11334,7 +11639,29 @@ emit_field_access = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, l
},
.enum_array, .array =>
{
#trap();
assert(string_equal(value.content.field_access.field_name, "length"));
>array_length_type = get_llvm_type(value.type, type_kind);
>array_element_count: u64 = 0;
switch (resolved_aggregate_type.id)
{
.enum_array =>
{
>enum_type = resolved_aggregate_type.content.enum_array.enum_type;
assert(enum_type.id == .enum);
array_element_count = enum_type.content.enum.fields.length;
},
.array =>
{
array_element_count = resolved_aggregate_type.content.array.element_count;
},
else => { unreachable; },
}
assert(array_element_count != 0);
>result = LLVMConstInt(array_length_type, array_element_count, 0);
return result;
},
else => { unreachable; },
}
@ -11802,6 +12129,29 @@ emit_condition = fn (module: &Module, condition: &Value) &LLVMValue
return llvm_condition;
}
emit_string_literal = fn (module: &Module, value: &Value) [2]&LLVMValue
{
assert(value.id == .string_literal);
>resolved_value_type = resolve_alias(module, value.type);
>null_terminate: u1 = 1;
>pointer = value.content.string_literal.pointer;
>length = value.content.string_literal.length;
>constant_string = LLVMConstStringInContext2(module.llvm.context, pointer, length, #extend(!null_terminate));
>u8_type = uint8(module);
resolve_type_in_place(module, u8_type);
>string_type = LLVMArrayType2(u8_type.llvm.abi, length + #extend(null_terminate));
>is_constant: u1 = 1;
>thread_local_mode: LLVMThreadLocalMode = .none;
>externally_initialized: u1 = 0;
>alignment: u32 = 1;
>global = llvm_create_global_variable(module.llvm.module, string_type, is_constant, .internal, constant_string, "conststring", thread_local_mode, externally_initialized, alignment, .global);
return [ global, LLVMConstInt(uint64(module).llvm.abi, length, 0) ];
}
ShortcircuitingOperation = enum
{
and,
@ -12096,6 +12446,17 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con
{
llvm_value = emit_intrinsic_call(module, ."llvm.va_end", [ module.llvm.pointer_type ][..], [ llvm_unary_value ][..]);
},
.enum_name =>
{
assert(type_kind == .abi);
>enum_type = resolved_unary_type;
assert(enum_type.id == .enum);
>enum_to_string_function = enum_type.content.enum.enum_to_string_function;
assert(enum_to_string_function != zero);
>call = LLVMBuildCall2(module.llvm.builder, LLVMGlobalGetValueType(enum_to_string_function), enum_to_string_function, &llvm_unary_value, 1, "");
LLVMSetInstructionCallConv(call, .fast);
llvm_value = call;
},
else => { #trap(); },
}
},
@ -12629,6 +12990,23 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con
LLVMSetInstructionCallConv(call, .fast);
llvm_value = call;
},
.string_literal =>
{
>string_literal = emit_string_literal(module, value);
switch (resolved_value_type.id)
{
.struct =>
{
assert(type_is_slice(resolved_value_type));
llvm_value = emit_slice_result(module, string_literal, resolved_value_type.llvm.abi);
},
.pointer =>
{
llvm_value = string_literal[0];
},
else => { unreachable; },
}
},
else =>
{
#trap();
@ -12639,29 +13017,6 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con
value.llvm = llvm_value;
}
emit_string_literal = fn (module: &Module, value: &Value) [2]&LLVMValue
{
assert(value.id == .string_literal);
>resolved_value_type = resolve_alias(module, value.type);
>null_terminate: u1 = 1;
>pointer = value.content.string_literal.pointer;
>length = value.content.string_literal.length;
>constant_string = LLVMConstStringInContext2(module.llvm.context, pointer, length, #extend(!null_terminate));
>u8_type = uint8(module);
resolve_type_in_place(module, u8_type);
>string_type = LLVMArrayType2(u8_type.llvm.abi, length + #extend(null_terminate));
>is_constant: u1 = 1;
>thread_local_mode: LLVMThreadLocalMode = .none;
>externally_initialized: u1 = 0;
>alignment: u32 = 1;
>global = llvm_create_global_variable(module.llvm.module, string_type, is_constant, .internal, constant_string, "conststring", thread_local_mode, externally_initialized, alignment, .global);
return [ global, LLVMConstInt(uint64(module).llvm.abi, length, 0) ];
}
emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, right: &Value) void
{
>parent_function_global: &Global = undefined;
@ -13471,6 +13826,441 @@ analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) v
LLVMClearInsertionPosition(module.llvm.builder);
}
},
.for =>
{
if (module.has_debug_info)
{
>lexical_block = LLVMDIBuilderCreateLexicalBlock(module.llvm.di_builder, statement.content.for.scope.parent.llvm, module.llvm.file, statement.content.for.scope.line, statement.content.for.scope.column);
statement.content.for.scope.llvm = lexical_block;
}
>index_type = uint64(module);
resolve_type_in_place(module, index_type);
>index_zero = LLVMConstNull(index_type.llvm.abi);
>entry_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "for_each.entry");
>body_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "for_each.body");
>continue_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "for_each.continue");
>exit_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "for_each.exit");
>previous_continue_block = module.llvm.continue_block;
>previous_exit_block = module.llvm.exit_block;
module.llvm.continue_block = continue_block;
module.llvm.exit_block = exit_block;
>kinds = statement.content.for.kinds;
>iteratables = statement.content.for.iteratables;
switch (statement.content.for.kind)
{
.slice =>
{
// Construct loop pre-initialization
assert(kinds.length == iteratables.length);
>local = statement.content.for.first_local;
for (i: 0..iteratables.length)
{
>kind = kinds[i];
>iteratable = iteratables[i];
analyze_type(module, iteratable, zero, zero);
if (iteratable.kind == .right)
{
if (!type_is_slice(iteratable.type))
{
reanalyze_type_as_left_value(module, iteratable);
}
}
>aggregate_type: &Type = zero;
if (iteratable.kind == .left and iteratable.type.id != .pointer)
{
if (!type_is_slice(iteratable.type))
{
report_error();
}
iteratable.kind = .right;
}
switch (iteratable.kind)
{
.left =>
{
>pointer_type = iteratable.type;
if (pointer_type.id != .pointer)
{
report_error();
}
aggregate_type = pointer_type.content.pointer.element_type;
},
.right =>
{
aggregate_type = iteratable.type;
assert(type_is_slice(aggregate_type));
},
}
>child_type: &Type = zero;
switch (aggregate_type.id)
{
.array =>
{
child_type = aggregate_type.content.array.element_type;
},
.struct =>
{
if (!type_is_slice(aggregate_type))
{
report_error();
}
>pointer_type = aggregate_type.content.struct.fields[0].type;
assert(pointer_type.id == .pointer);
child_type = pointer_type.content.pointer.element_type;
},
else => { unreachable; },
}
assert(child_type != zero);
assert(!local.variable.type);
>local_type = child_type;
if (kind == .left)
{
local_type = get_pointer_type(module, child_type);
}
assert(local_type != zero);
local.variable.type = local_type;
emit_local(module, local);
emit_value(module, iteratable, .memory, 0);
local = local.next;
}
assert(!local);
>length_value: &LLVMValue = zero;
// TODO: we are only taking the first length in order to operate
for (value: iteratables)
{
>value_type = value.type;
>aggregate_type = value_type;
if (value.kind == .left)
{
assert(value_type.id == .pointer);
aggregate_type = value_type.content.pointer.element_type;
}
assert(aggregate_type != zero);
>llvm_value = value.llvm;
switch (aggregate_type.id)
{
.array =>
{
assert(value.kind == .left);
>element_count = aggregate_type.content.array.element_count;
length_value = LLVMConstInt(index_type.llvm.abi, element_count, 0);
},
.struct =>
{
assert(type_is_slice(aggregate_type));
switch (value.kind)
{
.right =>
{
length_value = LLVMBuildExtractValue(module.llvm.builder, llvm_value, 1, "slice.length");
},
.left =>
{
>gep = LLVMBuildStructGEP2(module.llvm.builder, aggregate_type.llvm.abi, llvm_value, 1, "slice.length.pointer");
>load = create_load(module, {
.type = index_type,
.pointer = gep,
zero,
});
length_value = load;
},
}
},
else => { unreachable; },
}
break;
}
>index_alloca = create_alloca(module, {
.type = index_type,
.name = "for_each.index",
zero,
});
create_store(module, {
.source = index_zero,
.destination = index_alloca,
.type = index_type,
zero,
});
assert(length_value != zero);
// Construct loop header
LLVMBuildBr(module.llvm.builder, entry_block);
LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block);
>header_index_load = create_load(module, {
.type = index_type,
.pointer = index_alloca,
zero,
});
>index_compare = LLVMBuildICmp(module.llvm.builder, .ult, header_index_load, length_value, "");
LLVMBuildCondBr(module.llvm.builder, index_compare, body_block, exit_block);
// Construct loop body
LLVMPositionBuilderAtEnd(module.llvm.builder, body_block);
>body_index_load = create_load(module, {
.type = index_type,
.pointer = index_alloca,
zero,
});
local = statement.content.for.first_local;
for (i: 0..iteratables.length)
{
>kind = kinds[i];
>iteratable = iteratables[i];
>iteratable_type = iteratable.type;
>iteratable_kind = iteratable.kind;
>iteratable_llvm = iteratable.llvm;
>aggregate_type: &Type = iteratable_type;
if (iteratable_kind == .left)
{
assert(iteratable_type.id == .pointer);
aggregate_type = iteratable_type.content.pointer.element_type;
}
assert(aggregate_type != zero);
>element_pointer_value: &LLVMValue = zero;
switch (aggregate_type.id)
{
.array =>
{
assert(iteratable_kind == .left);
element_pointer_value = create_gep(module, {
.type = aggregate_type.llvm.memory,
.pointer = iteratable_llvm,
.indices = [ index_zero, body_index_load ][..],
zero,
});
},
.struct =>
{
#trap();
},
else => { unreachable; },
}
assert(element_pointer_value != zero);
>local_type = local.variable.type;
switch (kind)
{
.right =>
{
>evaluation_kind = get_evaluation_kind(local_type);
if (evaluation_kind == .scalar or type_is_slice(aggregate_type) or type_is_slice(local_type))
{
>load = create_load(module, {
.type = local_type,
.pointer = element_pointer_value,
zero,
});
create_store(module, {
.source = load,
.destination = local.variable.storage.llvm,
.type = local_type,
zero,
});
}
else
{
#trap();
}
},
.left =>
{
create_store(module, {
.source = element_pointer_value,
.destination = local.variable.storage.llvm,
.type = local_type,
zero,
});
},
}
local = local.next;
}
assert(!local);
analyze_statement(module, &statement.content.for.scope, statement.content.for.predicate);
if (LLVMGetInsertBlock(module.llvm.builder))
{
LLVMBuildBr(module.llvm.builder, continue_block);
}
LLVMPositionBuilderAtEnd(module.llvm.builder, continue_block);
>continue_index_load = create_load(module, {
.type = index_type,
.pointer = index_alloca,
zero,
});
>inc = LLVMBuildAdd(module.llvm.builder, continue_index_load, LLVMConstInt(index_type.llvm.abi, 1, 0), "");
create_store(module, {
.source = inc,
.destination = index_alloca,
.type = index_type,
zero,
});
LLVMBuildBr(module.llvm.builder, entry_block);
LLVMPositionBuilderAtEnd(module.llvm.builder, exit_block);
},
.range =>
{
>local = statement.content.for.first_local;
assert(local != zero);
assert(!local.next);
assert(!local.variable.type);
assert(kinds.length == 1);
if (iteratables.length == 2)
{
>start = iteratables[0];
>end = iteratables[1];
>local_type: &Type = zero;
if (value_is_constant(start))
{
#trap();
}
else
{
analyze_binary_type(module, start, end, 0, zero, 0);
assert(start.type == end.type);
local_type = start.type;
}
assert(local_type != zero);
for (iteratable: iteratables)
{
if (!iteratable.type)
{
analyze_type(module, iteratable, local_type, zero);
}
}
local.variable.type = local_type;
emit_local(module, local);
emit_value(module, start, .memory, 0);
>index_alloca = local.variable.storage.llvm;
create_store(module, {
.source = start.llvm,
.destination = index_alloca,
.type = local_type,
zero,
});
LLVMBuildBr(module.llvm.builder, entry_block);
LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block);
>header_index_load = create_load(module, {
.type = local_type,
.pointer = index_alloca,
zero,
});
emit_value(module, end, .abi, 0);
>length_value = end.llvm;
>index_compare = LLVMBuildICmp(module.llvm.builder, .ult, header_index_load, length_value, "");
LLVMBuildCondBr(module.llvm.builder, index_compare, body_block, exit_block);
LLVMPositionBuilderAtEnd(module.llvm.builder, body_block);
analyze_statement(module, &statement.content.for.scope, statement.content.for.predicate);
if (LLVMGetInsertBlock(module.llvm.builder))
{
LLVMBuildBr(module.llvm.builder, continue_block);
}
LLVMPositionBuilderAtEnd(module.llvm.builder, continue_block);
>continue_index_load = create_load(module, {
.type = local_type,
.pointer = index_alloca,
zero,
});
>inc = LLVMBuildAdd(module.llvm.builder, continue_index_load, LLVMConstInt(local_type.llvm.abi, 1, 0), "");
create_store(module, {
.source = inc,
.destination = index_alloca,
.type = local_type,
zero,
});
LLVMBuildBr(module.llvm.builder, entry_block);
LLVMPositionBuilderAtEnd(module.llvm.builder, exit_block);
}
else
{
// TODO: case for reverse range
#trap();
}
},
}
// END OF SCOPE
module.llvm.continue_block = previous_continue_block;
module.llvm.exit_block = previous_exit_block;
},
else =>
{
#trap();
@ -14918,6 +15708,10 @@ names: [_][]u8 =
"else_if_complicated",
"basic_shortcircuiting_if",
"shortcircuiting_if",
"field_access_left_assign",
"for_each",
"pointer_decay",
"enum_name",
];
[export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32

View File

@ -5172,7 +5172,7 @@ fn SliceEmitResult emit_string_literal(Module* module, Value* value)
LLVMThreadLocalMode tlm = LLVMNotThreadLocal;
bool externally_initialized = false;
u32 alignment = 1;
auto global = llvm_create_global_variable(module->llvm.module, string_type, is_constant, LLVMInternalLinkage, constant_string, string_literal("conststring"), tlm, externally_initialized, alignment, LLVMGlobalUnnamedAddr);
auto global = llvm_create_global_variable(module->llvm.module, string_type, is_constant, LLVMInternalLinkage, constant_string, string_literal("const.string"), tlm, externally_initialized, alignment, LLVMGlobalUnnamedAddr);
return { global, LLVMConstInt(uint64(module)->llvm.abi, length, false) };
} break;
@ -8944,6 +8944,8 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3
}
}
assert(!local);
analyze_statement(module, &statement->for_each.scope, statement->for_each.predicate, last_line, last_column, last_debug_location);
if (LLVMGetInsertBlock(module->llvm.builder))