From c5dc1a33b6e2266456eddd7144646d01c477af0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=C3=A4schle?= Date: Thu, 9 Jul 2020 17:14:14 +0200 Subject: [PATCH] all: remove `it` smartcast and replace with original variable name (#5764) --- cmd/tools/check_os_api_parity.v | 6 +- doc/docs.md | 46 +++++++++--- examples/lander.v | 2 +- vlib/v/ast/ast.v | 13 ++-- vlib/v/ast/str.v | 2 +- vlib/v/builder/builder.v | 4 +- vlib/v/checker/checker.v | 73 ++++++++++--------- .../tests/returns/return_missing_simple.vv | 7 ++ vlib/v/doc/doc.v | 8 +- vlib/v/fmt/fmt.v | 20 ++--- vlib/v/gen/cgen.v | 17 ++--- vlib/v/gen/comptime.v | 4 +- vlib/v/gen/js/js.v | 4 +- vlib/v/parser/assign.v | 4 +- vlib/v/parser/comptime.v | 9 +-- vlib/v/parser/fn.v | 2 +- vlib/v/parser/if.v | 19 ++++- vlib/v/parser/pratt.v | 4 + vlib/v/tests/if_smartcast_test.v | 60 +++++++++++++++ 19 files changed, 210 insertions(+), 94 deletions(-) create mode 100644 vlib/v/checker/tests/returns/return_missing_simple.vv create mode 100644 vlib/v/tests/if_smartcast_test.v diff --git a/cmd/tools/check_os_api_parity.v b/cmd/tools/check_os_api_parity.v index 96f490ff8b..1e3b2b7eca 100644 --- a/cmd/tools/check_os_api_parity.v +++ b/cmd/tools/check_os_api_parity.v @@ -98,9 +98,9 @@ fn (app App) gen_api_for_module_in_os(mod_name, os_name string) string { for f in b.parsed_files { for s in f.stmts { if s is ast.FnDecl { - if it.is_pub { - fn_signature := it.stringify(b.table) - fn_mod := it.modname() + if s.is_pub { + fn_signature := s.stringify(b.table) + fn_mod := s.modname() if fn_mod == mod_name { fline := '${fn_mod}: $fn_signature' res << fline diff --git a/doc/docs.md b/doc/docs.md index 2401d0f505..9c42270940 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -98,7 +98,7 @@ fn main() { ``` Save that snippet into a file `hello.v` . Now do: `v run hello.v` . -> That is assuming you have symlinked your V with `v symlink`, as described +> That is assuming you have symlinked your V with `v symlink`, as described [here](https://github.com/vlang/v/blob/master/README.md#symlinking). If you have not yet, you have to type the path to V manually. @@ -108,7 +108,7 @@ Congratulations - you just wrote your first V program, and executed it! See `v help` for all supported commands. In the above example, you can see that functions are declared with `fn`. -The return type goes after the function name. In this case `main` doesn't +The return type goes after the function name. In this case `main` doesn't return anything, so the return type can be omitted. As in many other languages (such as C, Go and Rust), `main` is an entry point. @@ -541,6 +541,32 @@ else { println(s) // "odd" ``` +#### Is check +You can check sum types using `if` like `match`ing them. +```v +struct Abc { + val string +} +struct Xyz { + foo string +} +type Alphabet = Abc | Xyz + +x := Alphabet(Abc{'test'}) // sum type +if x is Abc { + // x is automatically castet to Abc and can be used here + println(x) +} +``` + +If you have a struct field which should be checked, there is also a way to name a alias. +``` +if x.bar is MyStruct as bar { + // x.bar cannot be castet automatically, instead you say "as bar" which creates a variable with the MyStruct typing + println(bar) +} +``` + ### In operator `in` allows to check whether an array or a map contains an element. @@ -1851,9 +1877,9 @@ eprintln('$vm.name $vm.version\n $vm.description') ## Performance tuning -The generated C code is usually fast enough, when you compile your code -with `-prod`. There are some situations though, where you may want to give -additional hints to the C compiler, so that it can further optimize some +The generated C code is usually fast enough, when you compile your code +with `-prod`. There are some situations though, where you may want to give +additional hints to the C compiler, so that it can further optimize some blocks of code. NB: These are *rarely* needed, and should not be used, unless you @@ -1862,11 +1888,11 @@ To cite gcc's documentation: "programmers are notoriously bad at predicting how their programs actually perform". `[inline]` - you can tag functions with `[inline]`, so the C compiler will -try to inline them, which in some cases, may be beneficial for performance, +try to inline them, which in some cases, may be beneficial for performance, but may impact the size of your executable. -`if _likely_(bool expression) {` this hints the C compiler, that the passed -boolean expression is very likely to be true, so it can generate assembly +`if _likely_(bool expression) {` this hints the C compiler, that the passed +boolean expression is very likely to be true, so it can generate assembly code, with less chance of branch misprediction. In the JS backend, that does nothing. @@ -2106,7 +2132,7 @@ On Unix-like platforms, the file can be run directly after making it executable V has several attributes that modify the behavior of functions and structs. -An attribute is specified inside `[]` right before the function/struct declaration and applies only to the following definition. +An attribute is specified inside `[]` right before the function/struct declaration and applies only to the following definition. ```v // Calling this function will result in a deprecation warning @@ -2132,7 +2158,7 @@ fn bar() { } // For C interop only, tells V that the following struct is defined with `typedef struct` in C -[typedef] +[typedef] struct C.Foo { } // Declare a function with WINAPI diff --git a/examples/lander.v b/examples/lander.v index 462feb1f55..d1c81cda04 100644 --- a/examples/lander.v +++ b/examples/lander.v @@ -33,7 +33,7 @@ fn wait() { fn (l Lander) land(w World) { if w is Mars { - for it.dust_storm() { + for w.dust_storm() { wait() } } diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 51fb3bcd1a..3b069979e1 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -389,13 +389,13 @@ pub fn (i &Ident) var_info() IdentVar { pub struct InfixExpr { pub: - op token.Kind - pos token.Position - left Expr - right Expr + op token.Kind + pos token.Position + left Expr + right Expr pub mut: - left_type table.Type - right_type table.Type + left_type table.Type + right_type table.Type } pub struct PostfixExpr { @@ -443,6 +443,7 @@ pub: comments []Comment pub mut: smartcast bool // should only be true if cond is `x is sumtype`, it will be set in checker - if_expr + left_as_name string // only used in x is SumType check } pub struct LockExpr { diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index 1a0ce139bf..6662c673a7 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -279,7 +279,7 @@ pub fn (node Stmt) str() string { mut out := '' for i, left in it.left { if left is Ident { - var_info := it.var_info() + var_info := left.var_info() if var_info.is_mut { out += 'mut ' } diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index 545cacaf53..9023c549d4 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -288,8 +288,8 @@ fn (b &Builder) print_warnings_and_errors() { for file in b.parsed_files { for stmt in file.stmts { if stmt is ast.FnDecl { - if it.name == fn_name { - fline := it.pos.line_nr + if stmt.name == fn_name { + fline := stmt.pos.line_nr println('$file.path:$fline:') } } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 48fdb09d31..8486ef4ff1 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -207,17 +207,17 @@ fn (mut c Checker) check_file_in_main(file ast.File) bool { } ast.TypeDecl { if stmt is ast.AliasTypeDecl { - if it.is_pub { - c.warn('type alias `$it.name` $no_pub_in_main_warning', - it.pos) + if stmt.is_pub { + c.warn('type alias `$stmt.name` $no_pub_in_main_warning', + stmt.pos) } } else if stmt is ast.SumTypeDecl { - if it.is_pub { - c.warn('sum type `$it.name` $no_pub_in_main_warning', it.pos) + if stmt.is_pub { + c.warn('sum type `$stmt.name` $no_pub_in_main_warning', stmt.pos) } } else if stmt is ast.FnTypeDecl { - if it.is_pub { - c.warn('type alias `$it.name` $no_pub_in_main_warning', it.pos) + if stmt.is_pub { + c.warn('type alias `$stmt.name` $no_pub_in_main_warning', stmt.pos) } } } @@ -1266,25 +1266,25 @@ fn (mut c Checker) type_implements(typ, inter_typ table.Type, pos token.Position // return the actual type of the expression, once the optional is handled pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type table.Type) table.Type { if expr is ast.CallExpr { - if it.return_type.has_flag(.optional) { - if it.or_block.kind == .absent { + if expr.return_type.has_flag(.optional) { + if expr.or_block.kind == .absent { if ret_type != table.void_type { - c.error('${it.name}() returns an option, but you missed to add an `or {}` block to it', - it.pos) + c.error('${expr.name}() returns an option, but you missed to add an `or {}` block to it', + expr.pos) } } else { - c.check_or_expr(it.or_block, ret_type) + c.check_or_expr(expr.or_block, ret_type) } // remove optional flag // return ret_type.clear_flag(.optional) // TODO: currently unwrapped in assign, would need to refactor assign to unwrap here return ret_type - } else if it.or_block.kind == .block { - c.error('unexpected `or` block, the function `$it.name` does not return an optional', - it.pos) - } else if it.or_block.kind == .propagate { - c.error('unexpected `?`, the function `$it.name`, does not return an optional', - it.pos) + } else if expr.or_block.kind == .block { + c.error('unexpected `or` block, the function `$expr.name` does not return an optional', + expr.pos) + } else if expr.or_block.kind == .propagate { + c.error('unexpected `?`, the function `$expr.name`, does not return an optional', + expr.pos) } } return ret_type @@ -1522,7 +1522,7 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { } if assign_stmt.left.len != right_len { if right_first is ast.CallExpr { - c.error('assignment mismatch: $assign_stmt.left.len variable(s) but `${it.name}()` returns $right_len value(s)', + c.error('assignment mismatch: $assign_stmt.left.len variable(s) but `${right_first.name}()` returns $right_len value(s)', assign_stmt.pos) } else { c.error('assignment mismatch: $assign_stmt.left.len variable(s) $right_len value(s)', @@ -2620,27 +2620,30 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { // smartcast sumtypes when using `is` if branch.cond is ast.InfixExpr { infix := branch.cond as ast.InfixExpr - if infix.op == .key_is && infix.left is ast.Ident && infix.right is ast.Type { - left_expr := infix.left as ast.Ident + if infix.op == .key_is && (infix.left is ast.Ident || infix.left is ast.SelectorExpr) && infix.right is ast.Type { right_expr := infix.right as ast.Type - if left_expr.kind == .variable { - // Register shadow variable or `as` variable with actual type + is_variable := if infix.left is ast.Ident { + (infix.left as ast.Ident).kind == .variable + } else { true } + // Register shadow variable or `as` variable with actual type + if is_variable { left_sym := c.table.get_type_symbol(infix.left_type) - if left_sym.kind == .sum_type { + if left_sym.kind == .sum_type && branch.left_as_name.len > 0 { + mut is_mut := false + if infix.left is ast.Ident { + is_mut = (infix.left as ast.Ident).is_mut + } else if infix.left is ast.SelectorExpr { + selector := infix.left as ast.SelectorExpr + field := c.table.struct_find_field(left_sym, selector.field_name) or { table.Field{} } + is_mut = field.is_mut + } mut scope := c.file.scope.innermost(branch.body_pos.pos) - scope.register('it', ast.Var{ - name: 'it' + scope.register(branch.left_as_name, ast.Var{ + name: branch.left_as_name typ: right_expr.typ.to_ptr() - pos: left_expr.pos + pos: infix.left.position() is_used: true - is_mut: left_expr.is_mut - }) - scope.register(left_expr.name, ast.Var{ - name: left_expr.name - typ: right_expr.typ.to_ptr() - pos: left_expr.pos - is_used: true - is_mut: left_expr.is_mut + is_mut: is_mut }) node.branches[i].smartcast = true } diff --git a/vlib/v/checker/tests/returns/return_missing_simple.vv b/vlib/v/checker/tests/returns/return_missing_simple.vv new file mode 100644 index 0000000000..ad622126e3 --- /dev/null +++ b/vlib/v/checker/tests/returns/return_missing_simple.vv @@ -0,0 +1,7 @@ +fn foo() string { + if true { + return '' + } else { + + } +} \ No newline at end of file diff --git a/vlib/v/doc/doc.v b/vlib/v/doc/doc.v index 859e1fef85..0290b9f39d 100644 --- a/vlib/v/doc/doc.v +++ b/vlib/v/doc/doc.v @@ -45,7 +45,7 @@ pub fn merge_comments(stmts []ast.Stmt) string { mut res := []string{} for s in stmts { if s is ast.Comment { - res << it.text.trim_left('|') + res << s.text.trim_left('|') } } return res.join('\n') @@ -374,11 +374,11 @@ fn (mut d Doc) generate() ?Doc { continue } if stmt is ast.FnDecl { - if it.is_deprecated { + if stmt.is_deprecated { continue } - if it.receiver.typ != 0 { - node.attrs['parent'] = d.fmt.type_to_str(it.receiver.typ).trim_left('&') + if stmt.receiver.typ != 0 { + node.attrs['parent'] = d.fmt.type_to_str(stmt.receiver.typ).trim_left('&') p_idx := d.contents.index_by_name(node.attrs['parent']) if p_idx == -1 && node.attrs['parent'] != 'void' { d.contents << DocNode{ diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index a3f02f40b7..36edb8ca63 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -246,7 +246,7 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) { ast.AssignStmt { for i, left in node.left { if left is ast.Ident { - var_info := it.var_info() + var_info := left.var_info() if var_info.is_mut { f.write('mut ') } @@ -660,8 +660,8 @@ pub fn (mut f Fmt) prefix_expr_cast_expr(fexpr ast.Expr) { mut is_pe_amp_ce := false mut ce := ast.CastExpr{} if fexpr is ast.PrefixExpr { - if it.right is ast.CastExpr && it.op == .amp { - ce = it.right as ast.CastExpr + if fexpr.right is ast.CastExpr && fexpr.op == .amp { + ce = fexpr.right as ast.CastExpr ce.typname = f.table.get_type_symbol(ce.typ).name is_pe_amp_ce = true f.expr(ce) @@ -1248,16 +1248,16 @@ pub fn (mut f Fmt) call_expr(node ast.CallExpr) { } */ if node.left is ast.Ident { - it := node.left as ast.Ident + left := node.left as ast.Ident // `time.now()` without `time imported` is processed as a method call with `time` being // a `node.left` expression. Import `time` automatically. // TODO fetch all available modules - if it.name in ['time', 'os', 'strings', 'math', 'json', 'base64'] { - if it.name !in f.auto_imports { - f.auto_imports << it.name + if left.name in ['time', 'os', 'strings', 'math', 'json', 'base64'] { + if left.name !in f.auto_imports { + f.auto_imports << left.name f.file.imports << ast.Import{ - mod: it.name - alias: it.name + mod: left.name + alias: left.name } } // for imp in f.file.imports { @@ -1321,7 +1321,7 @@ pub fn (mut f Fmt) match_expr(it ast.MatchExpr) { stmt := branch.stmts[0] if stmt is ast.ExprStmt { // If expressions inside match branches can't be one a single line - if !expr_is_single_line(it.expr) { + if !expr_is_single_line(stmt.expr) { single_line = false break } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 551a3b5421..e0ce08a181 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -1213,12 +1213,12 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { mut blank_assign := false mut ident := ast.Ident{} if left is ast.Ident { - ident = it + ident = *left // id_info := ident.var_info() // var_type = id_info.typ - blank_assign = ident.kind == .blank_ident - if ident.info is ast.IdentVar { - share := (ident.info as ast.IdentVar).share + blank_assign = left.kind == .blank_ident + if left.info is ast.IdentVar { + share := (left.info as ast.IdentVar).share if share == .shared_t { var_type = var_type.set_flag(.shared_f) } @@ -1390,9 +1390,9 @@ fn (mut g Gen) gen_cross_tmp_variable(left []ast.Expr, val ast.Expr) { mut has_var := false for lx in left { if lx is ast.Ident { - if val.name == it.name { + if val.name == lx.name { g.write('_var_') - g.write(it.pos.pos.str()) + g.write(lx.pos.pos.str()) has_var = true break } @@ -2322,9 +2322,8 @@ fn (mut g Gen) if_expr(node ast.IfExpr) { infix := branch.cond as ast.InfixExpr right_type := infix.right as ast.Type left_type := infix.left_type - left_expr := infix.left as ast.Ident it_type := g.typ(right_type.typ) - g.write('\t$it_type* it = ($it_type*)') + g.write('\t$it_type* _sc_tmp_$branch.pos.pos = ($it_type*)') g.expr(infix.left) if left_type.is_ptr() { g.write('->') @@ -2332,7 +2331,7 @@ fn (mut g Gen) if_expr(node ast.IfExpr) { g.write('.') } g.writeln('obj;') - g.writeln('\t$it_type* $left_expr.name = it;') + g.writeln('\t$it_type* $branch.left_as_name = _sc_tmp_$branch.pos.pos;') } g.stmts(branch.stmts) } diff --git a/vlib/v/gen/comptime.v b/vlib/v/gen/comptime.v index 758e8a0024..931320153c 100644 --- a/vlib/v/gen/comptime.v +++ b/vlib/v/gen/comptime.v @@ -12,9 +12,9 @@ fn (g &Gen) comptime_call(node ast.ComptimeCall) { for stmt in node.vweb_tmpl.stmts { if stmt is ast.FnDecl { // insert stmts from vweb_tmpl fn - if it.name.starts_with('main.vweb_tmpl') { + if stmt.name.starts_with('main.vweb_tmpl') { g.inside_vweb_tmpl = true - g.stmts(it.stmts) + g.stmts(stmt.stmts) g.inside_vweb_tmpl = false break } diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 1a2f8b5e51..ad65157cfb 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -686,8 +686,8 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt) { val := stmt.right[i] mut is_mut := false if left is ast.Ident { - is_mut = it.is_mut - if it.kind == .blank_ident || it.name in ['', '_'] { + is_mut = left.is_mut + if left.kind == .blank_ident || left.name in ['', '_'] { tmp_var := g.new_tmp_var() // TODO: Can the tmp_var declaration be omitted? g.write('const $tmp_var = ') diff --git a/vlib/v/parser/assign.v b/vlib/v/parser/assign.v index 0ade268899..302028d190 100644 --- a/vlib/v/parser/assign.v +++ b/vlib/v/parser/assign.v @@ -15,7 +15,7 @@ fn (mut p Parser) check_undefined_variables(exprs []ast.Expr, val ast.Expr) { ast.Ident { for expr in exprs { if expr is ast.Ident { - if it.name == val.name { + if expr.name == val.name { p.error_with_pos('undefined variable: `$val.name`', val.pos) } } @@ -49,7 +49,7 @@ fn (mut p Parser) check_cross_variables(exprs []ast.Expr, val ast.Expr) bool { ast.Ident { for expr in exprs { if expr is ast.Ident { - if it.name == val_.name { + if expr.name == val_.name { return true } } diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index 67a0ee5c37..4b3c97bdc9 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -129,13 +129,12 @@ fn (mut p Parser) vweb() ast.ComptimeCall { // copy vars from current fn scope into vweb_tmpl scope for stmt in file.stmts { if stmt is ast.FnDecl { - if it.name == 'main.vweb_tmpl_$p.cur_fn_name' { - fn_decl := it - tmpl_scope := file.scope.innermost(it.body_pos.pos) + if stmt.name == 'main.vweb_tmpl_$p.cur_fn_name' { + tmpl_scope := file.scope.innermost(stmt.body_pos.pos) for _, obj in p.scope.objects { if obj is ast.Var { - mut v := it - v.pos = fn_decl.body_pos + mut v := obj + v.pos = stmt.body_pos tmpl_scope.register(v.name, *v) // set the controller action var to used // if its unused in the template it will warn diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index db5a11b0f9..7b66df8b11 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -555,7 +555,7 @@ fn (mut p Parser) fn_redefinition_error(name string) { fn have_fn_main(stmts []ast.Stmt) bool { for stmt in stmts { if stmt is ast.FnDecl { - if it.name == 'main.main' && it.mod == 'main' { + if stmt.name == 'main.main' && stmt.mod == 'main' { return true } } diff --git a/vlib/v/parser/if.v b/vlib/v/parser/if.v index 2a1fe29f2e..fa528f481d 100644 --- a/vlib/v/parser/if.v +++ b/vlib/v/parser/if.v @@ -64,9 +64,25 @@ fn (mut p Parser) if_expr() ast.IfExpr { } else { cond = p.expr(0) } + mut left_as_name := '' + is_infix := cond is ast.InfixExpr + if is_infix { + infix := cond as ast.InfixExpr + is_is_cast := infix.op == .key_is + is_ident := infix.left is ast.Ident + left_as_name = if is_is_cast && p.tok.kind == .key_as { + p.next() + p.check_name() + } else if is_ident { + ident := infix.left as ast.Ident + ident.name + } else { + '' + } + } end_pos := p.prev_tok.position() body_pos := p.tok.position() - p.inside_if = false + p.inside_if = false stmts := p.parse_block() if is_or { p.close_scope() @@ -77,6 +93,7 @@ fn (mut p Parser) if_expr() ast.IfExpr { pos: start_pos.extend(end_pos) body_pos: body_pos.extend(p.tok.position()) comments: comments + left_as_name: left_as_name } comments = [] if p.tok.kind != .key_else { diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index a6f1516e28..581f47e6c5 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -230,6 +230,10 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { } // continue on infix expr node = p.infix_expr(node) + // return early `if bar is SumType as b {` + if p.tok.kind == .key_as && p.inside_if { + return node + } } else if p.tok.kind in [.inc, .dec] { // Postfix node = ast.PostfixExpr{ diff --git a/vlib/v/tests/if_smartcast_test.v b/vlib/v/tests/if_smartcast_test.v new file mode 100644 index 0000000000..5c7ad1bc95 --- /dev/null +++ b/vlib/v/tests/if_smartcast_test.v @@ -0,0 +1,60 @@ +struct Abc { +mut: + val string +} +struct Xyz { + name string +} +type Alphabet = Abc | Xyz + +fn test_if_smartcast() { + x := Alphabet(Abc{'test'}) + if x is Abc { + assert x.val == 'test' + } +} + +fn test_mutable() { + mut x := Alphabet(Abc{'test'}) + if x is Abc { + y := Abc{} + x = &y + assert x == &y + } +} + +fn test_nested_if_smartcast() { + x := Alphabet(Abc{'test'}) + y := Alphabet(Xyz{'foo'}) + if x is Abc { + if y is Xyz { + assert y.name == 'foo' + } + } +} + +fn test_as_cast() { + x := Alphabet(Abc{'test'}) + if x is Abc as test { + assert test.val == 'test' + } +} + +struct Test { + abc Alphabet +} + +fn test_mutable_with_struct() { + mut x := Test{Abc{'test'}} + if x.abc is Abc as test { + test.val = 'test' + assert test.val == 'test' + } +} + +fn test_as_cast_with_struct() { + x := Test{Abc{'test'}} + if x.abc is Abc as test { + assert test.val == 'test' + } +}