diff --git a/.github/actions/cache-apt-packages-action/action.yml b/.github/actions/cache-apt-packages-action/action.yml new file mode 100644 index 0000000000..6331fce4ae --- /dev/null +++ b/.github/actions/cache-apt-packages-action/action.yml @@ -0,0 +1,30 @@ +name: 'Cache Apt Packages' +description: 'Cache all commonly used apt packages to speed up CI jobs' + +# imagemagick : convert, mogrify +# xvfb : xvfb +# openimageio-tools : idiff +# libxcursor-dev libxi-dev : V gfx deps +# libgl1-mesa-dri : For headless rendering on the CI / software DRI driver (LIBGL_ALWAYS_SOFTWARE=true) +# freeglut3-dev : Fixes graphic apps compilation with tcc +# sdl2 : needed for Puzzle Vibes & Chocolate Doom +# libsodium-dev : needed for Gitly and C2V + +runs: + using: 'composite' + steps: + - uses: awalsh128/cache-apt-pkgs-action@v1.5.3 + with: + version: 1.0 + packages: expect binutils postgresql sqlite3 clang valgrind \ + imagemagick openimageio-tools xvfb xsel xclip \ + libsodium-dev libpq-dev libssl-dev libsqlite3-dev \ + libfreetype6-dev libxi-dev libxcursor-dev \ + libgl-dev libxrandr-dev libasound2-dev \ + libx11-dev freeglut3-dev mesa-common-dev libgl1-mesa-dev libgl1-mesa-dri \ + libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev libsdl2-net-dev \ + libpng-dev libsamplerate0-dev \ + xfonts-75dpi xfonts-base + - name: Install common packages + run: echo "done installing packages" + shell: bash diff --git a/.github/workflows/compile_vlang_gui_examples.sh b/.github/workflows/compile_vlang_gui_examples.sh index bca98a7365..12f9809170 100755 --- a/.github/workflows/compile_vlang_gui_examples.sh +++ b/.github/workflows/compile_vlang_gui_examples.sh @@ -12,7 +12,7 @@ export VJOBS=1 show "Clone https://github.com/vlang/gui" v retry -- git clone --filter=blob:none --quiet https://github.com/vlang/gui ~/.vmodules/gui/ show "Checkout last known good commit" -git -C ~/.vmodules/gui/ checkout e5cc33fe816fef33d718cb1b91f66d6bd38fb4a4 +git -C ~/.vmodules/gui/ checkout b4e3716b042ee6352efedff64c5b92cbf0e81ded show "Check module for syntax and semantic errors" v -shared -check ~/.vmodules/gui show "Execute Tests" diff --git a/.github/workflows/puzzle_vibes_ci.yml b/.github/workflows/puzzle_vibes_ci.yml index 39aed68d37..29b7ba53ec 100644 --- a/.github/workflows/puzzle_vibes_ci.yml +++ b/.github/workflows/puzzle_vibes_ci.yml @@ -33,10 +33,7 @@ jobs: timeout-minutes: 20 steps: - uses: actions/checkout@v5 - - uses: awalsh128/cache-apt-pkgs-action@v1.5.3 - with: - packages: libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev - version: 1.0 + - uses: ./.github/actions/cache-apt-packages-action - name: Build V run: make && ./v symlink diff --git a/vlib/json/tests/json_decode_option_alias_test.v b/vlib/json/tests/json_decode_option_alias_test.v index f4c7c51b96..26a499503e 100644 --- a/vlib/json/tests/json_decode_option_alias_test.v +++ b/vlib/json/tests/json_decode_option_alias_test.v @@ -15,6 +15,6 @@ fn test_main() { assert data.str() == 'Alias(SomeStruct{ random_field_a: Option(none) random_field_b: Option(none) - empty_field: Option(none) + empty_field: Option(Empty{}) })' } diff --git a/vlib/v/gen/c/auto_eq_methods.v b/vlib/v/gen/c/auto_eq_methods.v index 7f58ad8e2e..497b507a55 100644 --- a/vlib/v/gen/c/auto_eq_methods.v +++ b/vlib/v/gen/c/auto_eq_methods.v @@ -214,21 +214,22 @@ fn (mut g Gen) gen_struct_equality_fn(left_type ast.Type) string { field_type := g.unwrap(field.typ) field_name := c_name(field.name) - left_arg := g.read_field(left_type, field_name, 'a') - right_arg := g.read_field(left_type, field_name, 'b') + mut left_arg := g.read_field(left_type, field_name, 'a') + mut right_arg := g.read_field(left_type, field_name, 'b') if field.typ.has_flag(.option) { - fn_builder.write_string('((${left_arg}.state == ${right_arg}.state && ${right_arg}.state == 2) || ') + fn_builder.write_string('((${left_arg}.state == ${right_arg}.state && ${right_arg}.state == 2) || (${left_arg}.state != 2 && ${right_arg}.state != 2 && (') } if field_type.sym.kind == .string { if field.typ.has_flag(.option) { - left_arg_opt := g.read_opt_field(left_type, field_name, 'a', field.typ) - right_arg_opt := g.read_opt_field(left_type, field_name, 'b', field.typ) - fn_builder.write_string('(((${left_arg_opt}).len == (${right_arg_opt}).len && (${left_arg_opt}).len == 0) || fast_string_eq(${left_arg_opt}, ${right_arg_opt}))') - } else if field.typ.is_ptr() { - fn_builder.write_string('((${left_arg}->len == ${right_arg}->len && ${left_arg}->len == 0) || fast_string_eq(*(${left_arg}), *(${right_arg})))') + left_arg = g.read_opt_field(left_type, field_name, 'a', field.typ) + right_arg = g.read_opt_field(left_type, field_name, 'b', field.typ) + } + + if field.typ.is_ptr() { + fn_builder.write_string('(${left_arg} == ${right_arg} || (${left_arg} != 0 && ${right_arg} != 0 && ((${left_arg})->len == (${right_arg})->len && (${left_arg})->len == 0) || fast_string_eq(*(${left_arg}), *(${right_arg}))))') } else { - fn_builder.write_string('((${left_arg}.len == ${right_arg}.len && ${left_arg}.len == 0) || fast_string_eq(${left_arg}, ${right_arg}))') + fn_builder.write_string('(((${left_arg}).len == (${right_arg}).len && (${left_arg}).len == 0) || fast_string_eq(${left_arg}, ${right_arg}))') } } else if field_type.sym.kind == .sum_type && !field.typ.is_ptr() { eq_fn := g.gen_sumtype_equality_fn(field.typ) @@ -260,7 +261,7 @@ fn (mut g Gen) gen_struct_equality_fn(left_type ast.Type) string { fn_builder.write_string('${eq_fn}_alias_eq(${left_arg}, ${right_arg})') } } else if field_type.sym.kind == .function && !field.typ.has_flag(.option) { - fn_builder.write_string('*((voidptr*)(${left_arg})) == *((voidptr*)(${right_arg}))') + fn_builder.write_string('((voidptr*)(${left_arg})) == ((voidptr*)(${right_arg}))') } else if field_type.sym.kind == .interface && (!field.typ.has_flag(.option) || !field.typ.is_ptr()) { ptr := if field.typ.is_ptr() { '*'.repeat(field.typ.nr_muls()) } else { '' } @@ -278,7 +279,7 @@ fn (mut g Gen) gen_struct_equality_fn(left_type ast.Type) string { fn_builder.write_string('${left_arg} == ${right_arg}') } if field.typ.has_flag(.option) { - fn_builder.write_string(')') + fn_builder.write_string(')))') } } } else { diff --git a/vlib/v/gen/c/json.v b/vlib/v/gen/c/json.v index 7b102074aa..df60727444 100644 --- a/vlib/v/gen/c/json.v +++ b/vlib/v/gen/c/json.v @@ -119,6 +119,19 @@ ${dec_fn_dec} { } } ') + + if utyp.has_flag(.option) { + dec.writeln('\tif (cJSON_IsNull(root)) {') + dec.writeln('\t${result_name}_${ret_styp} ret;') + dec.writeln('\t_result_ok(&res, (${result_name}*)&ret, sizeof(res));') + dec.writeln('\treturn ret;') + dec.writeln('\t}') + + base_type := utyp.clear_flag(.option) + base_type_str := g.styp(base_type) + dec.writeln('\t_option_ok(&(${base_type_str}[]){ ${g.type_default(base_type)} }, (${styp}*)&res, sizeof(${base_type_str}));\n') + } + extern_str := if g.pref.parallel_cc { 'extern ' } else { '' } g.json_forward_decls.writeln('${extern_str}${dec_fn_dec};') // Codegen encoder diff --git a/vlib/v/tests/structs/struct_option_field_comparison_test.v b/vlib/v/tests/structs/struct_option_field_comparison_test.v new file mode 100644 index 0000000000..7f88ae5e39 --- /dev/null +++ b/vlib/v/tests/structs/struct_option_field_comparison_test.v @@ -0,0 +1,26 @@ +pub struct SomeStruct { +pub mut: + test ?string +} + +pub struct MyStruct { +pub mut: + id string + result ?SomeStruct +} + +fn test_struct_with_option_fields_inequality() { + a := MyStruct{ + id: 'some id' + result: none + } + b := MyStruct{ + id: 'some id' + result: SomeStruct{} + } + dump(a) + dump(b) + dump(a == b) + dump(a != b) + assert a != b +} diff --git a/vlib/v/type_resolver/type_resolver.v b/vlib/v/type_resolver/type_resolver.v index a9d4072ef2..efd4f241c9 100644 --- a/vlib/v/type_resolver/type_resolver.v +++ b/vlib/v/type_resolver/type_resolver.v @@ -252,13 +252,19 @@ pub fn (mut t TypeResolver) get_type(node ast.Expr) ast.Type { return node.typ } else if node is ast.ComptimeCall { method_name := t.info.comptime_for_method.name - left_sym := t.table.sym(t.resolver.unwrap_generic(node.left_type)) - f := left_sym.find_method(method_name) or { - t.error('could not find method `${method_name}` on compile-time resolution', - node.method_pos) - return ast.void_type + left_type := t.resolver.unwrap_generic(node.left_type) + left_sym := t.table.sym(left_type) + if f := left_sym.find_method(method_name) { + return f.return_type + } else if left_sym.kind == .alias { + f := t.table.final_sym(left_type).find_method(method_name) or { + t.error('could not find method `${method_name}` on compile-time resolution', + node.method_pos) + return ast.void_type + } + return f.return_type } - return f.return_type + return ast.void_type } else if node is ast.IndexExpr && t.info.is_comptime(node.left) { nltype := t.get_type(node.left) nltype_unwrapped := t.resolver.unwrap_generic(nltype)