Merge branch 'vlang:master' into master

This commit is contained in:
kfont 2025-08-29 18:51:37 -07:00 committed by GitHub
commit 147d5b5709
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 321 additions and 66 deletions

View file

@ -1,3 +1,4 @@
// vtest retry: 3
import os
import time

View file

@ -6138,6 +6138,10 @@ that are substituted at compile time:
- `@BUILD_DATE` => replaced with the build date, for example '2024-09-13' .
- `@BUILD_TIME` => replaced with the build time, for example '12:32:07' .
- `@BUILD_TIMESTAMP` => replaced with the build timestamp, for example '1726219885' .
- `@OS` => replaced with the OS type, for example 'linux' .
- `@CCOMPILER` => replaced with the C compiler type, for example 'gcc' .
- `@BACKEND` => replaced with current language backend, for example 'c' or 'golang' .
- `@PLATFORM` => replaced with the platform type, for example 'amd64' .
Note: `@BUILD_DATE`, `@BUILD_TIME`, `@BUILD_TIMESTAMP` represent times in the UTC timezone.
By default, they are based on the current time of the compilation/build. They can be overridden
by setting the environment variable `SOURCE_DATE_EPOCH`. That is also useful while making

View file

@ -934,10 +934,8 @@ pub fn (fm FlagMapper) to_struct[T](defaults ?T) !T {
.f64()
} $else $if field.typ is bool {
if arg := f.arg {
if arg != '' {
return error('can not assign `${arg}` to bool field `${field.name}`')
}
}
result.$(field.name) = !the_default.$(field.name)
} $else $if field.typ is string {
trace_dbg_println('${@FN}: assigning (string) ${struct_name}.${field.name} = ${f.arg or {
@ -1037,7 +1035,7 @@ fn (mut fm FlagMapper) map_v(flag_ctx FlagContext, field StructField) !bool {
next := flag_ctx.next
if field.hints.has(.is_bool) {
if flag_name == field.match_name {
if flag_name == field.match_name || flag_name == field.short {
arg := if flag_raw.contains('=') { flag_raw.all_after('=') } else { '' }
if arg != '' {
return error('flag `${flag_raw}` can not be assigned to bool field "${field.name}"')

View file

@ -1,14 +1,16 @@
import flag
const args_bool_short = ['some.exe', '-h']
const args_bool_long = ['some.exe', '-help']
struct CliOptions {
show_help bool @[long: 'help'; short: h]
}
fn test_short_tail_bool() {
cli_options, unmatched := flag.to_struct[CliOptions](args_bool_short,
struct CliOptions2 {
show_help bool @[long: 'help'; short: h]
long string
}
fn test_v_style_short_tail_bool() {
cli_options, unmatched := flag.to_struct[CliOptions](['some.exe', '-h'],
skip: 1
style: .v
mode: .relaxed
@ -24,8 +26,8 @@ fn test_short_tail_bool() {
}
}
fn test_long_tail_bool() {
cli_options, unmatched := flag.to_struct[CliOptions](args_bool_long,
fn test_v_style_long_tail_bool() {
cli_options, unmatched := flag.to_struct[CliOptions](['some.exe', '-help'],
skip: 1
style: .v
mode: .relaxed
@ -40,3 +42,39 @@ fn test_long_tail_bool() {
assert false
}
}
fn test_v_style_short_bool() {
cli_options, unmatched := flag.to_struct[CliOptions2](['some.exe', '-h', '-long', 'val'],
skip: 1
style: .v
mode: .relaxed
)!
if unmatched.len > 0 {
assert false
}
if cli_options.show_help {
assert true
} else {
assert false
}
assert cli_options.long == 'val'
}
fn test_v_style_long_bool() {
cli_options, unmatched := flag.to_struct[CliOptions2](['some.exe', '-help', '-long', 'val'],
skip: 1
style: .v
mode: .relaxed
)!
if unmatched.len > 0 {
assert false
}
if cli_options.show_help {
assert true
} else {
assert false
}
assert cli_options.long == 'val'
}

View file

@ -158,10 +158,13 @@ pub fn (c TcpConn) read_ptr(buf_ptr &u8, len int) !int {
-1
}
}
$if trace_tcp ? {
eprintln('<<< TcpConn.read_ptr | c.sock.handle: ${c.sock.handle} | buf_ptr: ${ptr_str(buf_ptr)} len: ${len} | res: ${res}')
}
ecode := error_code()
if res > 0 {
$if trace_tcp ? {
eprintln(
'<<< TcpConn.read_ptr | c.sock.handle: ${c.sock.handle} | buf_ptr: ${ptr_str(buf_ptr)} | len: ${len} | res: ${res} |\n' +
unsafe { buf_ptr.vstring_with_len(len) })
}
$if trace_tcp_data_read ? {
eprintln(
'<<< TcpConn.read_ptr | 1 data.len: ${res:6} | hex: ${unsafe { buf_ptr.vbytes(res) }.hex()} | data: ' +
@ -169,7 +172,7 @@ pub fn (c TcpConn) read_ptr(buf_ptr &u8, len int) !int {
}
return res
}
code := if should_ewouldblock { int(error_ewouldblock) } else { error_code() }
code := if should_ewouldblock { int(error_ewouldblock) } else { ecode }
if code in [int(error_ewouldblock), int(error_eagain), C.EINTR] {
c.wait_for_read()!
res = $if is_coroutine ? {
@ -178,7 +181,9 @@ pub fn (c TcpConn) read_ptr(buf_ptr &u8, len int) !int {
C.recv(c.sock.handle, voidptr(buf_ptr), len, msg_dontwait)
}
$if trace_tcp ? {
eprintln('<<< TcpConn.read_ptr | c.sock.handle: ${c.sock.handle} | buf_ptr: ${ptr_str(buf_ptr)} len: ${len} | res: ${res}')
eprintln(
'<<< TcpConn.read_ptr | c.sock.handle: ${c.sock.handle} | buf_ptr: ${ptr_str(buf_ptr)} | len: ${len} | res: ${res} | code: ${code} |\n' +
unsafe { buf_ptr.vstring_with_len(len) })
}
$if trace_tcp_data_read ? {
if res > 0 {
@ -234,11 +239,11 @@ pub fn (mut c TcpConn) write_ptr(b &u8, len int) !int {
} $else {
C.send(c.sock.handle, ptr, remaining, msg_nosignal)
}
code := error_code()
$if trace_tcp_data_write ? {
eprintln('>>> TcpConn.write_ptr | data chunk, total_sent: ${total_sent:6}, remaining: ${remaining:6}, ptr: ${voidptr(ptr):x} => sent: ${sent:6}')
}
if sent < 0 {
code := error_code()
$if trace_tcp_send_failures ? {
eprintln('>>> TcpConn.write_ptr | send_failure, data.len: ${len:6}, total_sent: ${total_sent:6}, remaining: ${remaining:6}, ptr: ${voidptr(ptr):x}, c.write_timeout: ${c.write_timeout:3} => sent: ${sent:6}, error code: ${code:3}')
}
@ -456,9 +461,8 @@ pub fn (mut l TcpListener) accept_only() !&TcpConn {
} $else {
C.accept(l.sock.handle, 0, 0)
}
if !l.is_blocking && new_handle <= 0 {
code := error_code()
if !l.is_blocking && new_handle <= 0 {
if code in [int(error_einprogress), int(error_ewouldblock), int(error_eagain), C.EINTR] {
l.wait_for_accept()!
new_handle = $if is_coroutine ? {
@ -642,10 +646,10 @@ fn (mut s TcpSocket) connect(a Addr) ! {
} $else {
C.connect(s.handle, voidptr(&a), a.len())
}
ecode := error_code()
if res == 0 {
return
}
ecode := error_code()
// On nix non-blocking sockets we expect einprogress
// On windows we expect res == -1 && error_code() == ewouldblock
if (is_windows && ecode == int(error_ewouldblock)) || (!is_windows && res == -1

View file

@ -2190,16 +2190,15 @@ pub fn (mut t Table) unwrap_generic_type_ex(typ Type, generic_names []string, co
Interface {
// resolve generic types inside methods
mut imethods := ts.info.methods.clone()
gn_names := t.get_real_generic_names(typ, generic_names)
for mut method in imethods {
if unwrap_typ := t.convert_generic_type(method.return_type, generic_names,
concrete_types)
if unwrap_typ := t.convert_generic_type(method.return_type, gn_names,
concrete_types[..gn_names.len])
{
method.return_type = unwrap_typ
}
for mut param in method.params {
if unwrap_typ := t.convert_generic_type(param.typ, generic_names,
concrete_types)
{
if unwrap_typ := t.convert_generic_type(param.typ, gn_names, concrete_types) {
param.typ = unwrap_typ
}
}
@ -2289,6 +2288,7 @@ pub fn (mut t Table) generic_insts_to_concrete() {
fields[i].typ = t.unwrap_generic_type(fields[i].typ,
generic_names, info.concrete_types)
}
if t_typ := t.convert_generic_type(fields[i].typ, generic_names,
info.concrete_types)
{
@ -2464,6 +2464,18 @@ pub fn (mut t Table) generic_insts_to_concrete() {
}
}
// Extracts all generic names from Type<A>[B] => B when <A>[B] is present
// otherwise generic_names is returned
pub fn (t &Table) get_real_generic_names(typ Type, generic_names []string) []string {
if typ.has_flag(.generic) {
typ_name := t.type_to_str(typ)
if typ_name.contains('>[') {
return typ_name.split('>[')[1].all_before_last(']').split(',')
}
}
return generic_names
}
// Extracts all type names from given types, notice that MultiReturn will be decompose
// and will not included in returned string
pub fn (t &Table) get_generic_names(generic_types []Type) []string {

View file

@ -4030,6 +4030,18 @@ fn (mut c Checker) at_expr(mut node ast.AtExpr) ast.Type {
.build_timestamp {
node.val = util.stable_build_time.unix().str()
}
.os {
node.val = pref.get_host_os().lower()
}
.ccompiler {
node.val = c.pref.ccompiler_type.str()
}
.backend {
node.val = c.pref.backend.str()
}
.platform {
node.val = c.pref.arch.str()
}
.unknown {
c.error('unknown @ identifier: ${node.name}. Available identifiers: ${token.valid_at_tokens}',
node.pos)

View file

@ -1472,6 +1472,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
node.pos)
}
mut has_decompose := false
mut nr_multi_values := 0
for i, mut call_arg in node.args {
if func.params.len == 0 {
continue
@ -1482,21 +1483,22 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
if !func.is_variadic && has_decompose {
c.error('cannot have parameter after array decompose', node.pos)
}
param_i := i + nr_multi_values
param := if func.is_variadic && i >= func.params.len - 1 {
func.params.last()
} else {
func.params[i]
func.params[param_i]
}
// registers if the arg must be passed by ref to disable auto deref args
call_arg.should_be_ptr = param.typ.is_ptr() && !param.is_mut
if func.is_variadic && call_arg.expr is ast.ArrayDecompose {
if i > func.params.len - 1 {
if param_i > func.params.len - 1 {
c.error('too many arguments in call to `${func.name}`', node.pos)
}
}
has_decompose = call_arg.expr is ast.ArrayDecompose
already_checked := node.language != .js && call_arg.expr is ast.CallExpr
if func.is_variadic && i >= func.params.len - 1 {
if func.is_variadic && param_i >= func.params.len - 1 {
param_sym := c.table.sym(param.typ)
mut expected_type := param.typ
if param_sym.info is ast.Array {
@ -1688,15 +1690,16 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
multi_param := if func.is_variadic && i >= func.params.len - 1 {
func.params.last()
} else {
func.params[n + i]
func.params[n + param_i]
}
c.check_expected_call_arg(curr_arg, c.unwrap_generic(multi_param.typ),
node.language, call_arg) or {
c.error('${err.msg()} in argument ${i + n + 1} to `${fn_name}` from ${c.table.type_to_str(arg_typ)}',
c.error('${err.msg()} in argument ${param_i + n + 1} to `${fn_name}` from ${c.table.type_to_str(arg_typ)}',
call_arg.pos)
continue out
}
}
nr_multi_values += arg_typ_sym.info.types.len - 1
continue
} else if param_typ_sym.info is ast.Struct && arg_typ_sym.info is ast.Struct
&& param_typ_sym.info.is_anon {
@ -1788,13 +1791,14 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
continue
}
*/
c.error('${err.msg()} in argument ${i + 1} to `${fn_name}`', call_arg.pos)
c.error('${err.msg()} in argument ${i + nr_multi_values + 1} to `${fn_name}`',
call_arg.pos)
}
if final_param_sym.kind == .struct && arg_typ !in [ast.voidptr_type, ast.nil_type]
&& !c.check_multiple_ptr_match(arg_typ, param.typ, param, call_arg) {
got_typ_str, expected_typ_str := c.get_string_names_of(arg_typ, param.typ)
c.error('cannot use `${got_typ_str}` as `${expected_typ_str}` in argument ${i + 1} to `${fn_name}`',
call_arg.pos)
c.error('cannot use `${got_typ_str}` as `${expected_typ_str}` in argument ${i +
nr_multi_values + 1} to `${fn_name}`', call_arg.pos)
}
// Warn about automatic (de)referencing, which will be removed soon.
if func.language != .c && !c.inside_unsafe && !(call_arg.is_mut && param.is_mut) {
@ -2924,6 +2928,17 @@ fn (mut c Checker) check_expected_arg_count(mut node ast.CallExpr, f &ast.Fn) !
return error('')
}
}
} else if node.args.len > 1 && node.args.any(it.expr is ast.CallExpr
&& it.expr.nr_ret_values > 1) {
mut check_args := 0
for arg in node.args {
if arg.expr is ast.CallExpr && arg.expr.nr_ret_values > 0 {
check_args += arg.expr.nr_ret_values
} else {
check_args += 1
}
}
nr_args = check_args
}
if min_required_params < 0 {
min_required_params = 0
@ -2966,7 +2981,7 @@ fn (mut c Checker) check_expected_arg_count(mut node ast.CallExpr, f &ast.Fn) !
)
return error('')
} else if !f.is_variadic && nr_args > nr_params {
unexpected_args_pos := node.args[min_required_params].pos.extend(node.args.last().pos)
unexpected_args_pos := node.args[int_min(min_required_params, node.args.len - 1)].pos.extend(node.args.last().pos)
// c.error('3expected ${min_required_params} arguments, but got ${nr_args}', unexpected_args_pos)
c.fn_call_error_have_want(
nr_params: min_required_params

View file

@ -0,0 +1,18 @@
vlib/v/checker/tests/multi_return_arg_missing_err.vv:12:14: error: expected 2 arguments, but got 3
10 |
11 | fn main() {
12 | expect_2(1, returning_2())
| ~~~~~~~~~~~~~
13 | expect_2(returning_2(), 1)
14 | expect_4(returning_2(), returning_2())
Details: have (int literal, (int, int))
want (int, int)
vlib/v/checker/tests/multi_return_arg_missing_err.vv:13:26: error: expected 2 arguments, but got 3
11 | fn main() {
12 | expect_2(1, returning_2())
13 | expect_2(returning_2(), 1)
| ^
14 | expect_4(returning_2(), returning_2())
15 | }
Details: have ((int, int), int literal)
want (int, int)

View file

@ -0,0 +1,15 @@
fn expect_2(a int, b int) {
}
fn expect_4(a int, b int, c int, d int) {
}
fn returning_2() (int, int) {
return 1, 1
}
fn main() {
expect_2(1, returning_2())
expect_2(returning_2(), 1)
expect_4(returning_2(), returning_2())
}

View file

@ -5,24 +5,20 @@ vlib/v/checker/tests/multi_return_err.vv:18:10: error: cannot use `f64` as `int`
| ~~~~~~~~~~
19 | my_func3(my_func2(), 'foo')
20 | my_func4('foo', my_func2())
vlib/v/checker/tests/multi_return_err.vv:19:2: error: expected 3 arguments, but got 2
vlib/v/checker/tests/multi_return_err.vv:19:11: error: cannot use `f64` as `int` in argument 2 to `my_func3` from (int, f64)
17 | fn main() {
18 | my_func(my_func2())
19 | my_func3(my_func2(), 'foo')
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ~~~~~~~~~~
20 | my_func4('foo', my_func2())
21 | my_func(my_func5())
Details: have ((int, f64), string)
want (int, int, string)
vlib/v/checker/tests/multi_return_err.vv:20:2: error: expected 3 arguments, but got 2
vlib/v/checker/tests/multi_return_err.vv:20:18: error: cannot use `f64` as `int` in argument 3 to `my_func4` from (int, f64)
18 | my_func(my_func2())
19 | my_func3(my_func2(), 'foo')
20 | my_func4('foo', my_func2())
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ~~~~~~~~~~
21 | my_func(my_func5())
22 | my_func(my_func6())
Details: have (string, (int, f64))
want (string, int, int)
vlib/v/checker/tests/multi_return_err.vv:21:2: error: expected 2 arguments, but got 1
19 | my_func3(my_func2(), 'foo')
20 | my_func4('foo', my_func2())

View file

@ -1,18 +0,0 @@
import math
import os
import math
// keep comment
import gg
import gg { MouseButton }
import time { Duration }
import time { Duration }
import math.complex { Complex }
import math.complex { Complex }
const mypi = math.pi
const mb = MouseButton{}
const complex = Complex{}
fn main() {
println(os.path_separator)
}

View file

@ -1107,6 +1107,7 @@ pub fn (mut g Gen) get_sumtype_variant_name(typ ast.Type, sym ast.TypeSymbol) st
pub fn (mut g Gen) write_typeof_functions() {
g.writeln('')
g.writeln('// >> typeof() support for sum types / interfaces')
mut already_generated_ifaces := map[string]bool{}
for ityp, sym in g.table.type_symbols {
if sym.kind == .sum_type {
if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms {
@ -1177,6 +1178,10 @@ pub fn (mut g Gen) write_typeof_functions() {
if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms {
continue
}
if sym.cname in already_generated_ifaces {
continue
}
already_generated_ifaces[sym.cname] = true
g.definitions.writeln('${g.static_non_parallel}char * v_typeof_interface_${sym.cname}(int sidx);')
if g.pref.parallel_cc {
g.extern_out.writeln('extern char * v_typeof_interface_${sym.cname}(int sidx);')
@ -1805,11 +1810,15 @@ static inline void __${sym.cname}_pushval(${sym.cname} ch, ${push_arg} val) {
interface_non_generic_syms << sym
}
}
mut already_generated_ifaces := map[string]bool{}
for sym in interface_non_generic_syms {
if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms {
continue
}
if sym.cname !in already_generated_ifaces {
g.write_interface_typesymbol_declaration(sym)
already_generated_ifaces[sym.cname] = true
}
}
}
@ -7859,6 +7868,7 @@ fn (mut g Gen) interface_table() string {
}
mut sb := strings.new_builder(100)
mut conversion_functions := strings.new_builder(100)
mut already_generated_ifaces := map[string]bool{}
interfaces := g.table.type_symbols.filter(it.kind == .interface && it.info is ast.Interface)
for isym in interfaces {
inter_info := isym.info as ast.Interface
@ -7868,6 +7878,10 @@ fn (mut g Gen) interface_table() string {
if g.pref.skip_unused && isym.idx !in g.table.used_features.used_syms {
continue
}
if isym.cname in already_generated_ifaces {
continue
}
already_generated_ifaces[isym.cname] = true
// interface_name is for example Speaker
interface_name := isym.cname
// generate a struct that references interface methods

View file

@ -432,6 +432,9 @@ fn (mut w Walker) expr(node_ ast.Expr) {
if !w.uses_array && !w.is_direct_array_access {
w.uses_array = true
}
if node.elem_type.has_flag(.option) {
w.used_option++
}
}
ast.Assoc {
w.exprs(node.exprs)
@ -1180,6 +1183,9 @@ pub fn (mut w Walker) mark_by_sym(isym ast.TypeSymbol) {
w.mark_by_type(isym.info.key_type)
w.mark_by_type(isym.info.value_type)
w.features.used_maps++
if isym.info.value_type.has_flag(.option) {
w.used_option++
}
}
ast.Alias {
w.mark_by_type(isym.info.parent_type)
@ -1295,9 +1301,6 @@ fn (mut w Walker) mark_resource_dependencies() {
w.fn_by_name('strings.new_builder')
w.uses_free[ast.string_type] = true
}
if w.uses_eq {
w.fn_by_name('fast_string_eq')
}
if w.features.auto_str_ptr {
w.fn_by_name('isnil')
w.fn_by_name('tos4')
@ -1594,6 +1597,9 @@ pub fn (mut w Walker) finalize(include_panic_deps bool) {
w.mark_by_sym_name('StrIntpData')
w.mark_by_sym_name('StrIntpMem')
}
if w.uses_eq {
w.fn_by_name('fast_string_eq')
}
// remove unused symbols
w.remove_unused_fn_generic_types()

View file

@ -511,6 +511,10 @@ fn (mut p Parser) at() ast.AtExpr {
'@BUILD_DATE' { token.AtKind.build_date }
'@BUILD_TIME' { token.AtKind.build_time }
'@BUILD_TIMESTAMP' { token.AtKind.build_timestamp }
'@OS' { token.AtKind.os }
'@CCOMPILER' { token.AtKind.ccompiler }
'@BACKEND' { token.AtKind.backend }
'@PLATFORM' { token.AtKind.platform }
else { token.AtKind.unknown }
}
expr := ast.AtExpr{

View file

@ -310,6 +310,11 @@ fn (mut p Parser) import_syms(mut parent ast.Import) {
for p.tok.kind == .name {
pos := p.tok.pos()
alias := p.check_name()
if alias in p.imported_symbols {
p.error_with_pos('cannot register symbol `${alias}`, it was already imported',
pos)
return
}
p.imported_symbols[alias] = parent.mod + '.' + alias
// so we can work with this in fmt+checker
parent.syms << ast.ImportSymbol{

View file

@ -0,0 +1,3 @@
vlib/v/parser/tests/module_import_same_symbol2_err.vv:1:20: error: cannot register symbol `max`, it was already imported
1 | import math { max, max }
| ~~~

View file

@ -0,0 +1 @@
import math { max, max }

View file

@ -0,0 +1,4 @@
vlib/v/parser/tests/module_import_same_symbol_err.vv:2:17: error: cannot register symbol `max`, it was already imported
1 | import math { max }
2 | import arrays { max }
| ~~~

View file

@ -0,0 +1,2 @@
import math { max }
import arrays { max }

View file

@ -5,4 +5,5 @@ vlib/v/scanner/tests/unknown_comptime_var_err.vv:2:9: error: @ must be used befo
3 | }
Details: available compile time variables: @VROOT, @VMODROOT, @VEXEROOT, @FN, @METHOD, @MOD,
@STRUCT, @VEXE, @FILE, @DIR, @LINE, @COLUMN, @VHASH, @VCURRENTHASH, @VMOD_FILE, @VMODHASH,
@FILE_LINE, @LOCATION, @BUILD_DATE, @BUILD_TIME, @BUILD_TIMESTAMP
@FILE_LINE, @LOCATION, @BUILD_DATE, @BUILD_TIME, @BUILD_TIMESTAMP, @OS, @CCOMPILER,
@BACKEND, @PLATFORM

View file

@ -198,3 +198,27 @@ fn test_at_build_date_time_timestamp() {
now_utc := dump(time.utc().unix())
assert now_utc >= bts.i64()
}
fn test_at_os() {
println('Current OS is ${@OS}')
assert @OS in ['termux', 'android', 'wasm32_emscripten', 'linux', 'ios', 'macos', 'windows',
'freebsd', 'openbsd', 'netbsd', 'dragonfly', 'serenity', 'plan9', 'vinix', 'solaris', 'haiku',
'js_node', 'js_freestanding', 'js_browser']
}
fn test_at_ccompiler() {
println('Current C Compiler is ${@CCOMPILER}')
assert @CCOMPILER in ['gcc', 'tinyc', 'clang', 'emcc', 'mingw', 'msvc', 'cplusplus']
}
fn test_at_backend() {
println('Current language backend is ${@BACKEND}')
assert @BACKEND in ['c', 'golang', 'interpret', 'js_node', 'js_browser', 'js_freestanding',
'native', 'wasm']
}
fn test_at_platform() {
println('Current Platform is ${@PLATFORM}')
assert @PLATFORM in ['amd64', 'arm64', 'arm32', 'rv64', 'rv32', 'i386', 's390x', 'ppc64le',
'loongarch64', 'js_node', 'js_browser', 'js_freestanding', 'wasm32']
}

View file

@ -0,0 +1,79 @@
pub struct Range[T] {
pub:
from T
to T
inclusive bool
pub mut:
step T = T(1)
i T
mut:
started bool
}
pub fn (mut r Range[T]) next[T]() ?T {
if !r.started {
r.started = true
assert r.step > 0
if r.from > r.to {
r.step = -r.step
}
r.i = r.from
}
i := r.i
next_i := i + r.step
if r.inclusive {
if r.step < 0 && i < r.to {
return none
}
if r.step > 0 && i > r.to {
return none
}
} else {
if r.step < 0 && i <= r.to {
return none
}
if r.step > 0 && i >= r.to {
return none
}
}
r.i = next_i
return i
}
pub interface Iterator[T] {
mut:
next() ?T
}
pub struct Zip[A, B] {
mut:
a Iterator[A]
b Iterator[B]
}
pub struct Pair[A, B] {
a A
b B
}
pub fn (mut z Zip[A, B]) next[A, B]() ?Pair[A, B] {
a := z.a.next()?
b := z.b.next()?
return Pair[A, B]{a, b}
}
fn test_main() {
mut r1 := Range{
from: 5
to: 10
}
_ := Iterator[int](r1)
mut r2 := Range{
from: 10
to: 5
}
_ := Iterator[int](r2)
_ := Zip[int, int]{}
}

View file

@ -0,0 +1,6 @@
fn test_main() {
mut m := map[int]?u32{}
if c := m[0] {
println('c: ${c} not none!')
}
}

View file

@ -0,0 +1,6 @@
fn main() {
mut a := []?u32{len: 0xFF}
if c := a[0] {
println('c: ${c} not none!')
}
}

View file

@ -189,6 +189,10 @@ pub enum AtKind {
build_date
build_time
build_timestamp
os
ccompiler
backend
platform
}
pub const assign_tokens = [Kind.assign, .decl_assign, .plus_assign, .minus_assign, .mult_assign,
@ -197,7 +201,8 @@ pub const assign_tokens = [Kind.assign, .decl_assign, .plus_assign, .minus_assig
pub const valid_at_tokens = ['@VROOT', '@VMODROOT', '@VEXEROOT', '@FN', '@METHOD', '@MOD', '@STRUCT',
'@VEXE', '@FILE', '@DIR', '@LINE', '@COLUMN', '@VHASH', '@VCURRENTHASH', '@VMOD_FILE',
'@VMODHASH', '@FILE_LINE', '@LOCATION', '@BUILD_DATE', '@BUILD_TIME', '@BUILD_TIMESTAMP']
'@VMODHASH', '@FILE_LINE', '@LOCATION', '@BUILD_DATE', '@BUILD_TIME', '@BUILD_TIMESTAMP', '@OS',
'@CCOMPILER', '@BACKEND', '@PLATFORM']
pub const token_str = build_token_str()