all: remove `it` smartcast and replace with original variable name (#5764)

pull/5775/head
Daniel Däschle 2020-07-09 17:14:14 +02:00 committed by GitHub
parent 9511b7d0a1
commit c5dc1a33b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 210 additions and 94 deletions

View File

@ -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

View File

@ -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

View File

@ -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()
}
}

View File

@ -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 {

View File

@ -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 '
}

View File

@ -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:')
}
}

View File

@ -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
}

View File

@ -0,0 +1,7 @@
fn foo() string {
if true {
return ''
} else {
}
}

View File

@ -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{

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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 = ')

View File

@ -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
}
}

View File

@ -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

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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{

View File

@ -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'
}
}