Merge branch 'odin-lang:master' into master

This commit is contained in:
Maurice Elliott
2026-06-30 19:17:24 +01:00
committed by GitHub
19 changed files with 319 additions and 63 deletions

View File

@@ -146,6 +146,7 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
for i in 0..<count {
write_rune(&builder, decoded[i])
}
prev = decoded[count - 1]
continue
}
}

View File

@@ -134,6 +134,7 @@ register_user_marshaler :: proc(id: typeid, marshaler: User_Marshaler) -> Regist
return .None
}
@(require_results)
marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Marshal_Error) {
b := strings.builder_make(allocator, loc)
defer if err != nil {

View File

@@ -0,0 +1,68 @@
package encoding_json
Match_Key_Variant :: union #no_nil {
int, // Index
string, // Key
}
Match_Error :: enum {
None,
Invalid_Argument,
Invalid_Type_For_Index,
Invalid_Type_For_Key,
Key_Not_Found,
Out_Of_Bounds_Index,
}
Match_Flags :: distinct bit_set[Match_Flag]
Match_Flag :: enum {
Ignore_Key_Not_Found,
Allow_String_Indexing_By_Byte,
}
@(require_results)
match :: proc(value: Value, args: ..Match_Key_Variant, flags: Match_Flags = nil) -> (found: Value, err: Match_Error) {
found = value
arg_loop: for arg in args {
switch k in arg {
case int:
#partial switch v in found {
case Array:
if 0 <= k && k < len(v) {
found = v[k]
continue arg_loop
}
err = .Out_Of_Bounds_Index
return
case String:
if .Allow_String_Indexing_By_Byte in flags {
if 0 <= k && k < len(v) {
found = Integer(v[k])
continue arg_loop
}
err = .Out_Of_Bounds_Index
return
}
}
err = .Invalid_Type_For_Index
return
case string:
v, ok := found.(Object)
if !ok {
err = .Invalid_Type_For_Key
return
}
vfound, vok := v[k]
if vok || (.Ignore_Key_Not_Found in flags) {
found = vfound
continue arg_loop
}
err = .Key_Not_Found
return
case:
err = .Invalid_Argument
return
}
}
return
}

View File

@@ -14,9 +14,16 @@ Parser :: struct {
parse_integers: bool,
}
make_parser :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> Parser {
make_parser :: proc{
make_parser_from_bytes,
make_parser_from_string,
}
@(require_results)
make_parser_from_bytes :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> Parser {
return make_parser_from_string(string(data), spec, parse_integers, allocator)
}
@(require_results)
make_parser_from_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> Parser {
p: Parser
p.tok = make_tokenizer(data, spec, parse_integers)
@@ -27,11 +34,18 @@ make_parser_from_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, par
return p
}
parse :: proc{
parse_bytes,
parse_string,
}
parse :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) {
@(require_results)
parse_bytes :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) {
return parse_string(string(data), spec, parse_integers, allocator, loc)
}
@(require_results)
parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) {
context.allocator = allocator
p := make_parser_from_string(data, spec, parse_integers, allocator)
@@ -51,6 +65,7 @@ parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers
return parse_object(&p, loc)
}
@(require_results)
token_end_pos :: proc(tok: Token) -> Pos {
end := tok.pos
end.offset += len(tok.text)
@@ -65,6 +80,7 @@ advance_token :: proc(p: ^Parser) -> (Token, Error) {
}
@(require_results)
allow_token :: proc(p: ^Parser, kind: Token_Kind) -> bool {
if p.curr_token.kind == kind {
advance_token(p)
@@ -73,6 +89,7 @@ allow_token :: proc(p: ^Parser, kind: Token_Kind) -> bool {
return false
}
@(require_results)
expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error {
prev := p.curr_token
advance_token(p)
@@ -83,6 +100,7 @@ expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error {
}
@(require_results)
parse_colon :: proc(p: ^Parser) -> (err: Error) {
colon_err := expect_token(p, .Colon)
if colon_err == nil {
@@ -91,6 +109,7 @@ parse_colon :: proc(p: ^Parser) -> (err: Error) {
return .Expected_Colon_After_Key
}
@(require_results)
parse_comma :: proc(p: ^Parser) -> (do_break: bool) {
switch p.spec {
case .JSON5, .MJSON:
@@ -106,6 +125,7 @@ parse_comma :: proc(p: ^Parser) -> (do_break: bool) {
return false
}
@(require_results)
parse_value :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
err = .None
token := p.curr_token
@@ -176,6 +196,7 @@ parse_value :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err:
return
}
@(require_results)
parse_array :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
err = .None
expect_token(p, .Open_Bracket) or_return
@@ -203,7 +224,7 @@ parse_array :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err:
return
}
@(private)
@(private, require_results)
bytes_make :: proc(size, alignment: int, allocator: mem.Allocator, loc := #caller_location) -> (bytes: []byte, err: Error) {
b, berr := mem.alloc_bytes(size, alignment, allocator, loc)
if berr != nil {
@@ -217,6 +238,7 @@ bytes_make :: proc(size, alignment: int, allocator: mem.Allocator, loc := #calle
return
}
@(require_results)
clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_location) -> (str: string, err: Error) {
n := len(s)
b := bytes_make(n+1, 1, allocator, loc) or_return
@@ -228,6 +250,7 @@ clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_locatio
return
}
@(require_results)
parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator, loc := #caller_location) -> (key: string, err: Error) {
tok := p.curr_token
if p.spec != .JSON {
@@ -242,6 +265,7 @@ parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator, loc := #calle
return unquote_string(tok, p.spec, key_allocator, loc)
}
@(require_results)
parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc := #caller_location) -> (obj: Object, err: Error) {
obj = make(Object, allocator=p.allocator, loc=loc)
@@ -282,6 +306,7 @@ parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc := #caller_loca
return obj, .None
}
@(require_results)
parse_object :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
expect_token(p, .Open_Brace) or_return
obj := parse_object_body(p, .Close_Brace, loc) or_return
@@ -291,6 +316,7 @@ parse_object :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err:
// IMPORTANT NOTE(bill): unquote_string assumes a mostly valid string
@(require_results)
unquote_string :: proc(token: Token, spec: Specification, allocator := context.allocator, loc := #caller_location) -> (value: string, err: Error) {
get_u2_rune :: proc(s: string) -> rune {
if len(s) < 4 || s[0] != '\\' || s[1] != 'x' {

View File

@@ -49,11 +49,12 @@ Tokenizer :: struct {
curr_line_offset: int,
spec: Specification,
parse_integers: bool,
insert_comma: bool,
insert_comma: bool,
}
@(require_results)
make_tokenizer :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false) -> Tokenizer {
t := Tokenizer{pos = {line=1}, data = data, spec = spec, parse_integers = parse_integers}
next_rune(&t)
@@ -78,6 +79,7 @@ next_rune :: proc(t: ^Tokenizer) -> rune #no_bounds_check {
}
@(require_results)
get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
skip_digits :: proc(t: ^Tokenizer) {
for t.offset < len(t.data) {
@@ -101,6 +103,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
}
}
@(require_results)
scan_escape :: proc(t: ^Tokenizer) -> bool {
switch t.r {
case '"', '\'', '\\', '/', 'b', 'n', 'r', 't', 'f':
@@ -125,7 +128,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
return false
}
skip_whitespace :: proc(t: ^Tokenizer, on_newline: bool) -> rune {
skip_whitespace :: proc(t: ^Tokenizer, on_newline: bool) {
loop: for t.offset < len(t.data) {
switch t.r {
case ' ', '\t', '\v', '\f', '\r':
@@ -149,7 +152,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
break loop
}
}
return t.r
return
}
skip_to_next_line :: proc(t: ^Tokenizer) {
@@ -310,7 +313,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
break
}
if r == '\\' {
scan_escape(t)
_ = scan_escape(t)
}
}
@@ -347,10 +350,8 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
case '*':
// None-nested multi-line comments
for t.offset < len(t.data) {
next_rune(t)
if t.r == '*' {
next_rune(t)
if t.r == '/' {
if next_rune(t) == '*' {
if next_rune(t) == '/' {
next_rune(t)
return get_token(t)
}
@@ -385,6 +386,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
@(require_results)
is_valid_number :: proc(str: string, spec: Specification) -> bool {
s := str
if s == "" {
@@ -473,6 +475,7 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool {
return s == ""
}
@(require_results)
is_valid_string_literal :: proc(str: string, spec: Specification) -> bool {
s := str
if len(s) < 2 {

View File

@@ -112,6 +112,7 @@ destroy_value :: proc(value: Value, allocator := context.allocator, loc := #call
}
}
@(require_results)
clone_value :: proc(value: Value, allocator := context.allocator) -> Value {
value := value
context.allocator = allocator

View File

@@ -635,7 +635,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
defer p.allocator = allocator
p.allocator = mem.nil_allocator()
parse_value(p) or_return
_ = parse_value(p) or_return
if parse_comma(p) {
break struct_loop
}

View File

@@ -3,6 +3,7 @@ package encoding_json
import "core:mem"
// NOTE(bill): is_valid will not check for duplicate keys
@(require_results)
is_valid :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false) -> bool {
p := make_parser(data, spec, parse_integers, mem.nil_allocator())
@@ -21,6 +22,7 @@ is_valid :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers :=
return validate_object(&p)
}
@(require_results)
validate_object_key :: proc(p: ^Parser) -> bool {
if p.spec != .JSON {
if allow_token(p, .Ident) {
@@ -31,6 +33,7 @@ validate_object_key :: proc(p: ^Parser) -> bool {
return err == .None
}
@(require_results)
validate_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> bool {
for p.curr_token.kind != end_token {
if !validate_object_key(p) {
@@ -48,6 +51,7 @@ validate_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> bool {
return true
}
@(require_results)
validate_object :: proc(p: ^Parser) -> bool {
if err := expect_token(p, .Open_Brace); err != .None {
return false
@@ -61,6 +65,7 @@ validate_object :: proc(p: ^Parser) -> bool {
return true
}
@(require_results)
validate_array :: proc(p: ^Parser) -> bool {
if err := expect_token(p, .Open_Bracket); err != .None {
return false
@@ -83,6 +88,7 @@ validate_array :: proc(p: ^Parser) -> bool {
return true
}
@(require_results)
validate_value :: proc(p: ^Parser) -> bool {
token := p.curr_token

View File

@@ -296,8 +296,7 @@ gb_internal void big_int_from_string(BigInt *dst, String const &s, bool *success
gb_internal bool big_int_can_be_represented_in_64_bits(BigInt const *x) {
int bits_used = (x->used-1) * MP_DIGIT_BIT;
return bits_used <= 64;
return mp_count_bits(x) <= 64;
}
gb_internal u64 big_int_to_u64(BigInt const *x) {

View File

@@ -3421,6 +3421,8 @@ gb_internal void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *nod
x->expr = node;
x->value = exact_value_shift(be->op.kind, exact_value_to_integer(x->value), exact_value_to_integer(y->value));
check_is_expressible(c, x, x->type);
return;
}
@@ -5361,7 +5363,8 @@ gb_internal bool check_index_value(CheckerContext *c, Type *main_type, bool open
TEMPORARY_ALLOCATOR_GUARD();
String idx_str = big_int_to_string(temporary_allocator(), &i);
gbString expr_str = expr_to_string(operand.expr, temporary_allocator());
error(operand.expr, "Index '%s' is out of bounds range 0..<%lld, got %.*s", expr_str, max_count, LIT(idx_str));
char range_type = open_range ? '=' : '<';
error(operand.expr, "Index '%s' is out of bounds range 0..%c%lld, got %.*s", expr_str, range_type, max_count, LIT(idx_str));
return false;
}

View File

@@ -6894,7 +6894,10 @@ gb_internal void check_deferred_procedures(Checker *c) {
continue;
}
GB_ASSERT(is_type_proc(src->type));
if (!is_type_proc(src->type)) {
error(src->token, "Invalid procedure type found during deferred procedure checking");
continue;
}
GB_ASSERT(is_type_proc(dst->type));
Type *src_params = base_type(src->type)->Proc.params;
Type *src_results = base_type(src->type)->Proc.results;

View File

@@ -32,6 +32,22 @@ enum ExactValueKind {
ExactValue_Count,
};
gb_global char const *exact_value_kind_string[ExactValue_Count] = {
"Invalid",
"Bool",
"String",
"Integer",
"Float",
"Complex",
"Quaternion",
"Pointer",
"Compound",
"Procedure",
"Typeid",
"String16",
};
struct ExactValue {
ExactValueKind kind;
union {

View File

@@ -595,7 +595,7 @@ gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, As
gb_internal lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block);
gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValueRef *values, isize value_count_);
gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_);
gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_, bool force_non_named=false);
gb_internal void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name);
gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t);

View File

@@ -99,23 +99,47 @@ gb_internal LLVMValueRef llvm_const_cast(lbModule *m, LLVMValueRef val, LLVMType
return LLVMConstNull(dst);
}
GB_ASSERT_MSG(lb_sizeof(dst) == lb_sizeof(src), "%s vs %s", LLVMPrintTypeToString(dst), LLVMPrintTypeToString(src));
LLVMTypeKind kind = LLVMGetTypeKind(dst);
switch (kind) {
case LLVMPointerTypeKind: {
GB_ASSERT_MSG(lb_sizeof(dst) == lb_sizeof(src), "dst:%s vs src:%s (dst:%lld vs src:%lld)", LLVMPrintTypeToString(dst), LLVMPrintTypeToString(src),
cast(long long)lb_sizeof(dst),
cast(long long)lb_sizeof(src));
return LLVMConstPointerCast(val, dst);
}
case LLVMStructTypeKind: {
GB_ASSERT_MSG(lb_sizeof(dst) == lb_sizeof(src), "dst:%s vs src:%s (dst:%lld vs src:%lld)", LLVMPrintTypeToString(dst), LLVMPrintTypeToString(src),
cast(long long)lb_sizeof(dst),
cast(long long)lb_sizeof(src));
unsigned src_n = LLVMCountStructElementTypes(src);
unsigned dst_n = LLVMCountStructElementTypes(dst);
if (src_n != dst_n) goto failure;
if (LLVM_VERSION_MAJOR > 14) {
goto failure;
}
LLVMValueRef *field_vals = temporary_alloc_array<LLVMValueRef>(dst_n);
for (unsigned i = 0; i < dst_n; i++) {
LLVMValueRef field_val = llvm_const_extract_value(m, val, i);
if (field_val == nullptr) goto failure;
LLVMTypeRef dst_elem_ty = LLVMStructGetTypeAtIndex(dst, i);
LLVMTypeRef src_elem_ty = LLVMTypeOf(field_val);
if (lb_sizeof(dst_elem_ty) != lb_sizeof(src_elem_ty)) {
goto failure;
}
GB_ASSERT_MSG(lb_sizeof(dst_elem_ty) == lb_sizeof(src_elem_ty), "dst:%s vs src:%s (dst:%lld vs src:%lld) to %s from %s", LLVMPrintTypeToString(dst_elem_ty), LLVMPrintTypeToString(src_elem_ty),
cast(long long)lb_sizeof(dst_elem_ty),
cast(long long)lb_sizeof(src_elem_ty),
LLVMPrintTypeToString(dst),
LLVMPrintTypeToString(src)
);
field_vals[i] = llvm_const_cast(m, field_val, dst_elem_ty, failure_);
if (failure_ && *failure_) goto failure;
}
@@ -126,6 +150,9 @@ gb_internal LLVMValueRef llvm_const_cast(lbModule *m, LLVMValueRef val, LLVMType
return LLVMConstStructInContext(m->ctx, field_vals, dst_n, LLVMIsPackedStruct(dst));
}
}
case LLVMArrayTypeKind: {
goto failure;
}
}
failure:
@@ -212,10 +239,15 @@ gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValue
return llvm_const_named_struct_internal(m, struct_type, values_with_padding, values_with_padding_count);
}
gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_) {
gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_, bool force_non_named) {
unsigned value_count = cast(unsigned)value_count_;
unsigned elem_count = LLVMCountStructElementTypes(t);
GB_ASSERT_MSG(value_count == elem_count, "%s %u %u", LLVMPrintTypeToString(t), value_count, elem_count);
if (force_non_named) {
return LLVMConstStructInContext(m->ctx, values, value_count, true);
}
bool failure = false;
for (unsigned i = 0; i < elem_count; i++) {
LLVMTypeRef elem_type = LLVMStructGetTypeAtIndex(t, i);
@@ -911,6 +943,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
bool is_local = cc.allow_local && m->curr_procedure != nullptr;
if (is_type_union(type) && is_type_union_constantable(type)) {
Type *bt = base_type(type);
GB_ASSERT(bt->kind == Type_Union);
@@ -945,21 +978,26 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
res.type = original_type;
return res;
} else {
LLVMValueRef values[4] = {};
isize value_count = 0;
// Payload
values[value_count++] = cv.value;
unsigned tag_value = 1;
if (bt->Union.kind == UnionType_no_nil) {
tag_value = 0;
}
LLVMValueRef tag = LLVMConstInt(LLVMStructGetTypeAtIndex(llvm_type, 1), tag_value, false);
LLVMValueRef padding = nullptr;
isize value_count = 2;
// Tag
values[value_count++] = LLVMConstInt(LLVMStructGetTypeAtIndex(llvm_type, 1), tag_value, false);;
if (LLVMCountStructElementTypes(llvm_type) > 2) {
value_count = 3;
padding = LLVMConstNull(LLVMStructGetTypeAtIndex(llvm_type, 2));
GB_ASSERT(LLVMCountStructElementTypes(llvm_type) == 3);
// Padding
values[value_count++] = LLVMConstNull(LLVMStructGetTypeAtIndex(llvm_type, 2));
}
LLVMValueRef values[3] = {cv.value, tag, padding};
res.value = llvm_const_named_struct_internal(m, llvm_type, values, value_count);
res.type = original_type;
return res;
@@ -971,6 +1009,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
if (cl->elems.count == 0) {
return lb_const_nil(m, original_type);
}
value_type = type_of_expr(value.value_compound);
} else if (value.kind == ExactValue_Invalid) {
return lb_const_nil(m, original_type);
}
@@ -982,18 +1021,31 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
i64 block_size = bt->Union.variant_block_size;
if (are_types_identical(value_type, original_type)) {
while (are_types_identical(value_type, original_type)) {
if (value.kind == ExactValue_Compound) {
ast_node(cl, CompoundLit, value.value_compound);
if (cl->elems.count == 0) {
return lb_const_nil(m, original_type);
}
value_type = type_of_expr(value.value_compound);
if (!are_types_identical(value_type, original_type)) {
break;
}
GB_PANIC("%s --> %s vs %s",
expr_to_string(value.value_compound),
temp_canonical_string(value_type), temp_canonical_string(original_type));
} else if (value.kind == ExactValue_Invalid) {
return lb_const_nil(m, original_type);
}
GB_PANIC("%s vs %s", type_to_string(value_type), type_to_string(original_type));
GB_PANIC("(value.kind=%s) %s vs %s",
exact_value_kind_string[value.kind],
temp_canonical_string(value_type), temp_canonical_string(original_type));
}
// union_multiple_allow_compound:;
lbValue cv = lb_const_value(m, value_type, value, value_type, cc);
Type *variant_type = cv.type;
@@ -1001,16 +1053,16 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
LLVMValueRef values[4] = {};
unsigned value_count = 0;
#if LLVM_VERSION_MAJOR == 14
#if LLVM_VERSION_MAJOR == 14
LLVMTypeRef block_type = lb_type_internal_union_block_type(m, bt);
values[value_count++] = llvm_const_pad_to_size(m, cv.value, block_type);
#else
#else
values[value_count++] = cv.value;
if (type_size_of(variant_type) != block_size) {
if (block_size != type_size_of(variant_type)) {
LLVMTypeRef padding_type = lb_type_padding_filler(m, block_size - type_size_of(variant_type), 1);
values[value_count++] = LLVMConstNull(padding_type);
}
#endif
#endif
Type *tag_type = union_tag_type(bt);
LLVMTypeRef llvm_tag_type = lb_type(m, tag_type);
@@ -1026,6 +1078,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
}
res.value = LLVMConstStructInContext(m->ctx, values, value_count, true);
return res;
}
}
@@ -2031,6 +2084,8 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
LLVMTypeRef struct_type = lb_type(m, original_type);
bool force_non_named = false;
auto field_remapping = lb_get_struct_remapping(m, type);
unsigned value_count = LLVMCountStructElementTypes(struct_type);
@@ -2067,7 +2122,6 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
visited[index] = true;
}
unsigned idx_list_len = cast(unsigned)sel.index.count-1;
unsigned *idx_list = gb_alloc_array(temporary_allocator(), unsigned, idx_list_len);
@@ -2103,9 +2157,14 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
}
}
if (is_constant) {
LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, tav.type, cc).value;
LLVMValueRef elem_value = lb_const_value(m, cv_type, tav.value, tav.type, cc).value;
if (LLVMIsConstant(elem_value) && LLVMIsConstant(values[index])) {
values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len);
if (is_type_union(cv_type) || is_type_raw_union(cv_type)) {
force_non_named = true;
values[index] = llvm_const_insert_value_with_rebuild(m, values[index], elem_value, idx_list, idx_list_len);
} else {
values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len);
}
} else if (is_local) {
#if 1
lbProcedure *p = m->curr_procedure;
@@ -2152,13 +2211,6 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
Entity *f = type->Struct.fields[i+multiple_return_offset];
TypeAndValue tav = cl->elems[i]->tav;
// INFO: dead code:
// ExactValue val = {};
// if (tav.mode != Addressing_Invalid) {
// val = tav.value;
// }
if (is_type_tuple(tav.type)){
multiple_return_offset += tav.type->Tuple.variables.count-1;
}
@@ -2198,7 +2250,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
}
if (is_constant) {
res.value = llvm_const_named_struct_internal(m, struct_type, values, cast(unsigned)value_count);
res.value = llvm_const_named_struct_internal(m, struct_type, values, cast(unsigned)value_count, force_non_named);
LLVMTypeRef res_type = LLVMTypeOf(res.value);
GB_ASSERT(lb_sizeof(res_type) == lb_sizeof(struct_type));
return res;

View File

@@ -461,6 +461,27 @@ gb_internal LLVMValueRef llvm_const_insert_value(lbModule *m, LLVMValueRef agg,
return extracted_value;
}
gb_internal LLVMValueRef llvm_const_insert_value_with_rebuild(lbModule *m, LLVMValueRef agg, LLVMValueRef val, unsigned *indices, isize count) {
GB_ASSERT(LLVMIsConstant(agg));
GB_ASSERT(LLVMIsConstant(val));
GB_ASSERT(count > 0);
unsigned value_count = LLVMCountStructElementTypes(LLVMTypeOf(agg));
LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, count);
defer (gb_free(heap_allocator(), values));
for (unsigned i = 0; i < value_count; i++) {
values[i] = llvm_const_extract_value(m, agg, i);
}
if (count == 1) {
values[indices[0]] = val;
} else {
values[indices[0]] = llvm_const_insert_value_with_rebuild(m, values[indices[0]], val, indices+1, count-1);
}
return LLVMConstStructInContext(m->ctx, values, value_count, true);
}
@@ -1647,12 +1668,14 @@ gb_internal lbValue lb_emit_union_tag_ptr(lbProcedure *p, lbValue u) {
unsigned element_count = LLVMCountStructElementTypes(uvt);
GB_ASSERT_MSG(element_count >= 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt));
LLVMValueRef ptr = u.value;
ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(uvt, 0), "");
lbValue tag_ptr = {};
tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 1, "");
tag_ptr.type = alloc_type_pointer(tag_type);
tag_ptr.value = LLVMBuildPointerCast(p->builder, tag_ptr.value, lb_type(p->module, tag_ptr.type), "");
return tag_ptr;
}
@@ -2402,8 +2425,9 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
i64 full_type_align = type_align_of(type);
GB_ASSERT(full_type_size % full_type_align == 0);
if (type->Struct.is_raw_union) {
bool requires_packing = type->Struct.is_packed;
if (type->Struct.is_raw_union) {
lbStructFieldRemapping field_remapping = {};
slice_init(&field_remapping, permanent_allocator(), 1);
@@ -2411,7 +2435,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
fields[0] = lb_type_padding_filler(m, full_type_size, full_type_align);
field_remapping[0] = 0;
LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false);
LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), requires_packing);
map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping);
map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);
return struct_type;
@@ -2431,7 +2455,6 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
}
i64 prev_offset = 0;
bool requires_packing = type->Struct.is_packed;
for (i32 field_index : struct_fields_index_by_increasing_offset(temporary_allocator(), type)) {
Entity *field = type->Struct.fields[field_index];
i64 offset = type->Struct.offsets[field_index];
@@ -2497,8 +2520,9 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
LLVMTypeRef fields[] = {lb_type(m, type->Union.variants[0])};
return LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false);
}
bool is_packed = false;
auto fields = array_make<LLVMTypeRef>(temporary_allocator(), 0, 3);
auto fields = array_make<LLVMTypeRef>(temporary_allocator(), 0, 4);
if (is_type_union_maybe_pointer(type)) {
LLVMTypeRef variant = lb_type(m, type->Union.variants[0]);
array_add(&fields, variant);
@@ -2514,6 +2538,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align);
array_add(&fields, padding_type);
}
is_packed = true;
} else {
LLVMTypeRef block_type = lb_type_internal_union_block_type(m, type);
@@ -2526,9 +2551,10 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align);
array_add(&fields, padding_type);
}
is_packed = true;
}
return LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, false);
return LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, is_packed);
}
break;
@@ -2639,6 +2665,13 @@ gb_internal LLVMTypeRef lb_type(lbModule *m, Type *type) {
m->internal_type_level += 1;
llvm_type = lb_type_internal(m, type);
m->internal_type_level -= 1;
// {
// i64 tsz = type_size_of(type);
// i64 lsz = lb_sizeof(llvm_type);
// GB_ASSERT_MSG(tsz == lsz, "%s %lld vs %lld %s", type_to_string(type), cast(long long)tsz, cast(long long)lsz, LLVMPrintTypeToString(llvm_type));
// }
if (m->internal_type_level == 0) {
map_set(&m->types, type, llvm_type);
}

View File

@@ -4762,14 +4762,22 @@ gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr, lbValue *sret_
return res;
}
gb_internal void lb_add_values_to_array(lbProcedure *p, Array<lbValue> *args, lbValue value) {
gb_internal void lb_add_values_to_array(lbProcedure *p, Array<lbValue> *args, lbValue value, Type *c_vararg_type = nullptr) {
if (is_type_tuple(value.type)) {
for_array(i, value.type->Tuple.variables) {
lbValue sub_value = lb_emit_struct_ev(p, value, cast(i32)i);
array_add(args, sub_value);
if (c_vararg_type) {
array_add(args, lb_emit_c_vararg(p, sub_value, c_vararg_type));
} else {
array_add(args, sub_value);
}
}
} else {
array_add(args, value);
if (c_vararg_type) {
array_add(args, lb_emit_c_vararg(p, value, c_vararg_type));
} else {
array_add(args, value);
}
}
}
@@ -4890,9 +4898,9 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbVal
if (is_type_untyped_nil(arg.type)) {
arg = lb_const_nil(p->module, t_rawptr);
}
array_add(&args, lb_emit_c_vararg(p, arg, arg.type));
lb_add_values_to_array(p, &args, arg, arg.type);
} else {
array_add(&args, lb_emit_c_vararg(p, arg, elem_type));
lb_add_values_to_array(p, &args, arg, elem_type);
}
}
break;
@@ -5018,15 +5026,15 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbVal
if (is_type_untyped_nil(arg.type)) {
arg = lb_const_nil(p->module, t_rawptr);
}
array_add(&args, lb_emit_c_vararg(p, arg, arg.type));
lb_add_values_to_array(p, &args, arg, arg.type);
} else {
array_add(&args, lb_emit_c_vararg(p, arg, elem_type));
lb_add_values_to_array(p, &args, arg, elem_type);
}
}
} else {
lbValue value = lb_build_expr(p, fv->value);
GB_ASSERT(!is_type_tuple(value.type));
array_add(&args, lb_emit_c_vararg(p, value, value.type));
lb_add_values_to_array(p, &args, value, value.type);
}
} else {
lbValue value = lb_build_expr(p, fv->value);

View File

@@ -761,17 +761,27 @@ gb_internal void find_visual_studio_paths_from_env_vars(Find_Result *result) {
gb_internal Find_Result find_visual_studio_and_windows_sdk() {
Find_Result r = {};
find_windows_kit_paths(&r);
find_visual_studio_by_fighting_through_microsoft_craziness(&r);
// Prefer the toolset exported into the environment (e.g. by vsdevcmd) so the
// linker uses the same MSVC toolset of other externally compiled C++ dependencies.
// The COM autodetection grabs the first installed Visual Studio, which may be an older
// toolset whose runtime libraries lack symbols that the newer-built libraries
// reference. Fall back to autodetection when the environment is bare.
find_visual_studio_paths_from_env_vars(&r);
bool vs_found =
r.vs_exe_path.len &&
r.vs_library_path.len ;
if (!vs_found) {
find_visual_studio_by_fighting_through_microsoft_craziness(&r);
}
bool sdk_found =
r.windows_sdk_bin_path.len &&
r.windows_sdk_um_library_path.len &&
r.windows_sdk_ucrt_library_path.len ;
bool vs_found =
r.vs_exe_path.len &&
r.vs_library_path.len ;
if (!sdk_found) {
find_windows_kit_paths_from_env_vars(&r);
}

View File

@@ -1471,6 +1471,10 @@ gb_internal bool is_type_constant_type(Type *t) {
return is_type_constant_type(t->Array.elem);
case Type_EnumeratedArray:
return is_type_constant_type(t->EnumeratedArray.elem);
case Type_SimdVector:
return is_type_constant_type(t->SimdVector.elem);
case Type_Matrix:
return is_type_constant_type(t->Matrix.elem);
}
return false;
}
@@ -2699,8 +2703,6 @@ gb_internal bool is_type_union_constantable(Type *type) {
if (bt->Union.variants.count == 0) {
return true;
} else if (bt->Union.variants.count == 1) {
return is_type_constant_type(bt->Union.variants[0]);
}
for (Type *v : bt->Union.variants) {

View File

@@ -1,5 +1,6 @@
package test_core_xml
import "core:encoding/entity"
import "core:encoding/xml"
import "core:testing"
import "core:strings"
@@ -217,6 +218,29 @@ run_test :: proc(t: ^testing.T, test: TEST, loc := #caller_location) {
}
}
@(test)
test_normalize_whitespace :: proc(t: ^testing.T) {
s := "A &amp; B"
normalized_entity_decode, _ := entity.decode_xml(s, {.Normalize_Whitespace})
defer delete(normalized_entity_decode)
testing.expect_value(t, normalized_entity_decode, "A & B")
s = `<hellope attr="A &amp; B">A &amp; B</hellope>`
opts := xml.Options{
flags = {.Ignore_Unsupported, .Decode_SGML_Entities},
}
doc, err := xml.parse_bytes(transmute([]byte)s, opts)
defer xml.destroy(doc)
assert(err == .None)
testing.expect_value(t, doc.elements[0].value[0], "A & B")
attr := doc.elements[0].attribs
testing.expect_value(t, attr[0].val, "A & B")
}
@(private)
doc_to_string :: proc(doc: ^xml.Document) -> (result: string) {
/*
@@ -298,4 +322,4 @@ doc_to_string :: proc(doc: ^xml.Document) -> (result: string) {
print(strings.to_writer(&buf), doc)
return strings.clone(strings.to_string(buf))
}
}