diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 7ad3ef1d6..c62301c34 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -597,8 +597,9 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info { base := info loop: for { #partial switch i in base.variant { - case Type_Info_Named: base = i.base - case Type_Info_Enum: base = i.base + case Type_Info_Named: base = i.base + case Type_Info_Enum: base = i.base + case Type_Info_Bit_Field: base = i.backing_type case: break loop } } diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 3e215e0f2..be541befa 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -617,7 +617,7 @@ field_flag_strings := [Field_Flag]string{ .Any_Int = "#any_int", .Subtype = "#subtype", .By_Ptr = "#by_ptr", - .No_Broadcast ="#no_broadcast", + .No_Broadcast = "#no_broadcast", .Results = "results", .Tags = "field tag", @@ -842,6 +842,23 @@ Matrix_Type :: struct { elem: ^Expr, } +Bit_Field_Type :: struct { + using node: Expr, + tok_pos: tokenizer.Pos, + backing_type: ^Expr, + open: tokenizer.Pos, + fields: []^Bit_Field_Field, + close: tokenizer.Pos, +} + +Bit_Field_Field :: struct { + using node: Node, + docs: ^Comment_Group, + name: ^Expr, + type: ^Expr, + bit_size: ^Expr, + comments: ^Comment_Group, +} Any_Node :: union { ^Package, @@ -898,6 +915,7 @@ Any_Node :: union { ^Map_Type, ^Relative_Type, ^Matrix_Type, + ^Bit_Field_Type, ^Bad_Stmt, ^Empty_Stmt, @@ -928,6 +946,7 @@ Any_Node :: union { ^Attribute, ^Field, ^Field_List, + ^Bit_Field_Field, } @@ -982,6 +1001,7 @@ Any_Expr :: union { ^Map_Type, ^Relative_Type, ^Matrix_Type, + ^Bit_Field_Type, } diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin index d105f6035..bca740dd4 100644 --- a/core/odin/ast/clone.odin +++ b/core/odin/ast/clone.odin @@ -336,6 +336,13 @@ clone_node :: proc(node: ^Node) -> ^Node { case ^Relative_Type: r.tag = clone(r.tag) r.type = clone(r.type) + case ^Bit_Field_Type: + r.backing_type = clone(r.backing_type) + r.fields = auto_cast clone(r.fields) + case ^Bit_Field_Field: + r.name = clone(r.name) + r.type = clone(r.type) + r.bit_size = clone(r.bit_size) case: fmt.panicf("Unhandled node kind: %v", r) } diff --git a/core/odin/ast/walk.odin b/core/odin/ast/walk.odin index 966a8137e..63107a2e2 100644 --- a/core/odin/ast/walk.odin +++ b/core/odin/ast/walk.odin @@ -414,7 +414,15 @@ walk :: proc(v: ^Visitor, node: ^Node) { walk(v, n.row_count) walk(v, n.column_count) walk(v, n.elem) - + case ^Bit_Field_Type: + walk(v, n.backing_type) + for f in n.fields { + walk(v, f) + } + case ^Bit_Field_Field: + walk(v, n.name) + walk(v, n.type) + walk(v, n.bit_size) case: fmt.panicf("ast.walk: unexpected node type %T", n) } diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index ab723e145..9eaef4655 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -531,7 +531,7 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool { return is_semicolon_optional_for_node(p, n.type) case ^ast.Pointer_Type: return is_semicolon_optional_for_node(p, n.elem) - case ^ast.Struct_Type, ^ast.Union_Type, ^ast.Enum_Type: + case ^ast.Struct_Type, ^ast.Union_Type, ^ast.Enum_Type, ^ast.Bit_Set_Type, ^ast.Bit_Field_Type: // Require semicolon within a procedure body return p.curr_proc == nil case ^ast.Proc_Lit: @@ -2790,6 +2790,48 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { mt.column_count = column_count mt.elem = elem return mt + + case .Bit_Field: + tok := expect_token(p, .Bit_Field) + + backing_type := parse_type_or_ident(p) + if backing_type == nil { + token := advance_token(p) + error(p, token.pos, "Expected a backing type for a 'bit_field'") + } + + skip_possible_newline_for_literal(p) + open := expect_token_after(p, .Open_Brace, "bit_field") + + fields: [dynamic]^ast.Bit_Field_Field + for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF { + name := parse_ident(p) + expect_token(p, .Colon) + type := parse_type(p) + expect_token(p, .Or) + bit_size := parse_expr(p, true) + + field := ast.new(ast.Bit_Field_Field, name.pos, bit_size) + + field.name = name + field.type = type + field.bit_size = bit_size + + append(&fields, field) + + allow_token(p, .Comma) or_break + } + + close := expect_closing_brace_of_field_list(p) + + bf := ast.new(ast.Bit_Field_Type, tok.pos, close.pos) + + bf.tok_pos = tok.pos + bf.backing_type = backing_type + bf.open = open.pos + bf.fields = fields[:] + bf.close = close.pos + return bf case .Asm: tok := expect_token(p, .Asm) @@ -2897,7 +2939,8 @@ is_literal_type :: proc(expr: ^ast.Expr) -> bool { ^ast.Map_Type, ^ast.Bit_Set_Type, ^ast.Matrix_Type, - ^ast.Call_Expr: + ^ast.Call_Expr, + ^ast.Bit_Field_Type: return true } return false diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 7dd208a49..571e4001d 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -445,7 +445,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { for value in v.values { #partial switch a in value.derived { - case ^ast.Union_Type, ^ast.Enum_Type, ^ast.Struct_Type: + case ^ast.Union_Type, ^ast.Enum_Type, ^ast.Struct_Type, ^ast.Bit_Field_Type: add_semicolon = false || called_in_stmt case ^ast.Proc_Lit: add_semicolon = false @@ -488,6 +488,37 @@ visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, options := List_Options{}) { } } +@(private) +visit_bit_field_fields :: proc(p: ^Printer, list: []^ast.Bit_Field_Field, options := List_Options{}) { + if len(list) == 0 { + return + } + + // we have to newline the expressions to respect the source + for v, i in list { + // Don't move the first expression, it looks bad + if i != 0 && .Enforce_Newline in options { + newline_position(p, 1) + } else if i != 0 { + move_line_limit(p, v.pos, 1) + } + + visit_expr(p, v.name, options) + push_generic_token(p, .Colon, 0) + visit_expr(p, v.type, options) + push_generic_token(p, .Or, 1) + visit_expr(p, v.bit_size, options) + + if (i != len(list) - 1 || .Trailing in options) && .Add_Comma in options { + push_generic_token(p, .Comma, 0) + } + } + + if len(list) > 1 && .Enforce_Newline in options { + newline_position(p, 1) + } +} + @(private) visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { if len(attributes) == 0 { @@ -1293,6 +1324,25 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { visit_expr(p, v.column_count) push_generic_token(p, .Close_Bracket, 0) visit_expr(p, v.elem) + case ^ast.Bit_Field_Type: + push_generic_token(p, .Bit_Field, 1) + + visit_expr(p, v.backing_type) + + if len(v.fields) == 0 || v.pos.line == v.close.line { + push_generic_token(p, .Open_Brace, 1) + visit_bit_field_fields(p, v.fields, {.Add_Comma}) + push_generic_token(p, .Close_Brace, 0) + } else { + visit_begin_brace(p, v.pos, .Generic, len(v.fields)) + newline_position(p, 1) + set_source_position(p, v.fields[0].pos) + visit_bit_field_fields(p, v.fields, {.Add_Comma, .Trailing, .Enforce_Newline}) + set_source_position(p, v.close) + visit_end_brace(p, v.close) + } + + set_source_position(p, v.close) case: panic(fmt.aprint(expr.derived)) } diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 37f953c58..4b54f0ed1 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -2074,7 +2074,13 @@ SRWLOCK_INIT :: SRWLOCK{} STARTF_USESTDHANDLES: DWORD : 0x00000100 VOLUME_NAME_DOS: DWORD : 0x0 -MOVEFILE_REPLACE_EXISTING: DWORD : 1 + +MOVEFILE_COPY_ALLOWED: DWORD: 0x2 +MOVEFILE_CREATE_HARDLINK: DWORD: 0x10 +MOVEFILE_DELAY_UNTIL_REBOOT: DWORD: 0x4 +MOVEFILE_FAIL_IF_NOT_TRACKABLE: DWORD: 0x20 +MOVEFILE_REPLACE_EXISTING: DWORD : 0x1 +MOVEFILE_WRITE_THROUGH: DWORD: 0x8 FILE_BEGIN: DWORD : 0 FILE_CURRENT: DWORD : 1 diff --git a/core/time/datetime/validation.odin b/core/time/datetime/validation.odin index 110a7e78e..87d5aa1cd 100644 --- a/core/time/datetime/validation.odin +++ b/core/time/datetime/validation.odin @@ -56,9 +56,9 @@ validate_hour_minute_second :: proc "contextless" (#any_int hour, #any_int minut return .None } -validate_datetime :: proc "contextless" (using datetime: DateTime) -> (err: Error) { - validate(date) or_return - validate(time) or_return +validate_datetime :: proc "contextless" (datetime: DateTime) -> (err: Error) { + validate(datetime.date) or_return + validate(datetime.time) or_return return .None } @@ -69,4 +69,4 @@ validate :: proc{ validate_hour_minute_second, validate_time, validate_datetime, -} \ No newline at end of file +} diff --git a/src/check_type.cpp b/src/check_type.cpp index 81e67f261..f1d991acb 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1689,7 +1689,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para bool is_using = (p->flags&FieldFlag_using) != 0; if ((check_vet_flags(param) & VetFlag_UsingParam) && is_using) { ERROR_BLOCK(); - error(param, "'using' on a procedure parameter is now allowed when '-vet' or '-vet-using-param' is applied"); + error(param, "'using' on a procedure parameter is not allowed when '-vet' or '-vet-using-param' is applied"); error_line("\t'using' is considered bad practice to use as a statement/procedure parameter outside of immediate refactoring\n"); } diff --git a/src/linker.cpp b/src/linker.cpp index aa36b3278..498a96c5f 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -384,7 +384,7 @@ gb_internal i32 linker_stage(LinkerData *gen) { LIT(obj_file), LIT(build_context.extra_assembler_flags) ); - if (!result) { + if (result) { gb_printf_err("executing `nasm` to assemble foreing import of %.*s failed.\n\tSuggestion: `nasm` does not ship with the compiler and should be installed with your system's package manager.\n", LIT(asm_file)); return result; } diff --git a/tests/core/odin/test_parser.odin b/tests/core/odin/test_parser.odin index 3837436bc..08f73a732 100644 --- a/tests/core/odin/test_parser.odin +++ b/tests/core/odin/test_parser.odin @@ -1,9 +1,12 @@ package test_core_odin_parser -import "core:testing" import "core:fmt" -import "core:os" +import "core:odin/ast" import "core:odin/parser" +import "core:odin/printer" +import "core:os" +import "core:strings" +import "core:testing" TEST_count := 0 @@ -30,6 +33,7 @@ when ODIN_TEST { main :: proc() { t := testing.T{} test_parse_demo(&t) + test_parse_bitfield(&t) fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) if TEST_fail > 0 { @@ -47,4 +51,44 @@ test_parse_demo :: proc(t: ^testing.T) { for key, value in pkg.files { expect(t, value.syntax_error_count == 0, fmt.tprintf("%v should contain zero errors", key)) } -} \ No newline at end of file +} + +@test +test_parse_bitfield :: proc(t: ^testing.T) { + file := ast.File{ + fullpath = "test.odin", + src = ` +package main + +Foo :: bit_field uint {} + +Foo :: bit_field uint {hello: bool | 1} + +Foo :: bit_field uint { + hello: bool | 1, + hello: bool | 5, +} + +// Hellope 1. +Foo :: bit_field uint { + // Hellope 2. + hello: bool | 1, + hello: bool | 5, // Hellope 3. +} + `, + } + + p := parser.default_parser() + ok := parser.parse_file(&p, &file) + expect(t, ok == true, "bad parse") + + cfg := printer.default_style + cfg.newline_style = .LF + print := printer.make_printer(cfg) + out := printer.print(&print, &file) + + tsrc := strings.trim_space(file.src) + tout := strings.trim_space(out) + + expect(t, tsrc == tout, fmt.tprintf("\n%s\n!=\n%s", tsrc, tout)) +} diff --git a/vendor/OpenGL/wrappers.odin b/vendor/OpenGL/wrappers.odin index 550ba3cfa..a04df6987 100644 --- a/vendor/OpenGL/wrappers.odin +++ b/vendor/OpenGL/wrappers.odin @@ -787,8 +787,8 @@ when !GL_DEBUG { fmt.printf(" call: gl%s(", loc.procedure) { // add input arguments - for arg, i in args[num_ret:] { - if i > 0 { fmt.printf(", ") } + for arg, arg_index in args[num_ret:] { + if arg_index > 0 { fmt.printf(", ") } if v, ok := arg.(u32); ok { // TODO: Assumes all u32 are GLenum (they're not, GLbitfield and GLuint are also mapped to u32), fix later by better typing if err == .INVALID_ENUM { @@ -806,8 +806,8 @@ when !GL_DEBUG { fmt.printf(") -> %v \n", args[0]) } else if num_ret > 1 { fmt.printf(") -> (") - for arg, i in args[1:num_ret] { - if i > 0 { fmt.printf(", ") } + for arg, arg_index in args[1:num_ret] { + if arg_index > 0 { fmt.printf(", ") } fmt.printf("%v", arg) } fmt.printf(")\n")