parser/checker: deduce type of global from initialization expression (#11005)

pull/11009/head
Uwe Krüger 2021-07-31 15:35:19 +02:00 committed by GitHub
parent c1f3eb6014
commit 6068777e03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 111 additions and 36 deletions

View File

@ -100,6 +100,16 @@ pub fn (s &Scope) find_var(name string) ?&Var {
return none
}
pub fn (s &Scope) find_global(name string) ?&GlobalField {
if obj := s.find(name) {
match obj {
GlobalField { return &obj }
else {}
}
}
return none
}
pub fn (s &Scope) find_const(name string) ?&ConstField {
if obj := s.find(name) {
match obj {

View File

@ -134,10 +134,24 @@ pub fn (mut c Checker) check(ast_file &ast.File) {
}
}
}
for stmt in ast_file.stmts {
for mut stmt in ast_file.stmts {
if stmt is ast.ConstDecl || stmt is ast.ExprStmt {
c.expr_level = 0
c.stmt(stmt)
}
}
for mut stmt in ast_file.stmts {
if stmt is ast.GlobalDecl {
c.expr_level = 0
c.stmt(stmt)
}
}
for mut stmt in ast_file.stmts {
if stmt !is ast.ConstDecl && stmt !is ast.GlobalDecl && stmt !is ast.ExprStmt {
c.expr_level = 0
c.stmt(stmt)
}
}
c.check_scope_vars(c.file.scope)
}
@ -4831,13 +4845,12 @@ fn (mut c Checker) global_decl(mut node ast.GlobalDecl) {
if sym.kind == .placeholder {
c.error('unknown type `$sym.name`', field.typ_pos)
}
if field.expr !is ast.EmptyExpr {
expr_typ := c.expr(field.expr)
if !c.check_types(expr_typ, field.typ) {
got_sym := c.table.get_type_symbol(expr_typ)
c.error('cannot initialize global variable `$field.name` of type `$sym.name` with expression of type `$got_sym.name`',
field.expr.position())
if field.has_expr {
field.typ = c.expr(field.expr)
mut v := c.file.global_scope.find_global(field.name) or {
panic('internal compiler error - could not find global in scope')
}
v.typ = c.table.mktyp(field.typ)
}
c.global_names << field.name
}
@ -5569,7 +5582,7 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
c.mark_as_referenced(mut &node.expr, true)
}
}
} else if node.typ == ast.bool_type && !c.inside_unsafe {
} else if node.typ == ast.bool_type && node.expr_type != ast.bool_type && !c.inside_unsafe {
c.error('cannot cast to bool - use e.g. `some_int != 0` instead', node.pos)
} else if node.expr_type == ast.none_type && !node.typ.has_flag(.optional) {
type_name := c.table.type_to_str(node.typ)

View File

@ -1,3 +0,0 @@
vlib/v/checker/tests/globals/assign_no_type.vv:1:21: error: global assign must have a type and value, use `name = type(value)` or `name type`
1 | __global ( test = 0 )
| ^

View File

@ -1 +0,0 @@
__global ( test = 0 )

View File

@ -1,3 +1,3 @@
vlib/v/checker/tests/globals/assign_no_value.vv:1:23: error: global assign must have a type and value, use `name = type(value)` or `name type`
vlib/v/checker/tests/globals/assign_no_value.vv:1:19: error: undefined ident: `int`
1 | __global ( test = int )
| ^
| ~~~

View File

@ -3,9 +3,9 @@ vlib/v/checker/tests/globals/unknown_typ.vv:1:12: error: unknown type `foo`
| ~~~
2 | __global (
3 | y = float(5.0)
vlib/v/checker/tests/globals/unknown_typ.vv:3:6: error: unknown type `float`
vlib/v/checker/tests/globals/unknown_typ.vv:3:6: error: unknown function: float
1 | __global x foo
2 | __global (
3 | y = float(5.0)
| ~~~~~
| ~~~~~~~~~~
4 | )

View File

@ -983,10 +983,7 @@ pub fn (mut f Fmt) global_decl(node ast.GlobalDecl) {
f.write(strings.repeat(` `, max - field.name.len))
if field.has_expr {
f.write('= ')
f.write(f.table.type_to_str_using_aliases(field.typ, f.mod2alias))
f.write('(')
f.expr(field.expr)
f.write(')')
} else {
f.write('${f.table.type_to_str_using_aliases(field.typ, f.mod2alias)}')
}

View File

@ -2937,24 +2937,47 @@ fn (mut p Parser) global_decl() ast.GlobalDecl {
pos := p.tok.position()
name := p.check_name()
has_expr := p.tok.kind == .assign
mut expr := ast.empty_expr()
mut typ := ast.void_type
mut typ_pos := token.Position{}
if has_expr {
p.next() // =
}
typ_pos := p.tok.position()
typ := p.parse_type()
if p.tok.kind == .assign {
p.error('global assign must have the type around the value, use `name = type(value)`')
return ast.GlobalDecl{}
}
mut expr := ast.empty_expr()
if has_expr {
if p.tok.kind != .lpar {
p.error('global assign must have a type and value, use `name = type(value)` or `name type`')
return ast.GlobalDecl{}
}
p.next() // (
expr = p.expr(0)
p.check(.rpar)
match expr {
ast.CastExpr {
typ = (expr as ast.CastExpr).typ
}
ast.StructInit {
typ = (expr as ast.StructInit).typ
}
ast.ArrayInit {
typ = (expr as ast.ArrayInit).typ
}
ast.ChanInit {
typ = (expr as ast.ChanInit).typ
}
ast.BoolLiteral, ast.IsRefType {
typ = ast.bool_type
}
ast.CharLiteral {
typ = ast.char_type
}
ast.FloatLiteral {
typ = ast.f64_type
}
ast.IntegerLiteral, ast.SizeOf {
typ = ast.int_type
}
ast.StringLiteral, ast.StringInterLiteral {
typ = ast.string_type
}
else {
// type will be deduced by checker
}
}
} else {
typ_pos = p.tok.position()
typ = p.parse_type()
}
field := ast.GlobalField{
name: name

View File

@ -28,6 +28,30 @@ fn test_global_init() {
assert true
}
fn get_u64() u64 {
return 27
}
fn test_no_type() {
assert test == 0
assert typeof(test).name == 'int'
assert testf == 1.25
assert typeof(testf).name == 'f64'
assert testneg == -2
assert typeof(testneg).name == 'int'
assert testnegf == -1250000
assert typeof(testnegf).name == 'f64'
assert testexpl == 7
assert typeof(testexpl).name == 'f32'
assert testfn == 27
assert typeof(testfn).name == 'u64'
assert typeof(testarr).name == '[]f64'
assert testarr.len == 10
assert testarr[9] == 2.75
assert typeof(testmap).name == 'map[string]f64'
assert testmap['asd'] == -7.25
}
__global (
intmap map[string]int
numberfns map[string]fn () int
@ -38,6 +62,18 @@ __global (
mtx sync.RwMutex
f1 = f64(545 / (sizeof(f64) + f32(8))) // directly initialized
f2 f64
test = 0 // int
testf = 1.25 // f64
testneg = -2 // int
testnegf = -1.25e06 // f64
testexpl = f32(7)
testfn = get_u64()
testarr = []f64{len: 10, init: 2.75}
testmap = map{
'qwe': 2.5
'asd': -7.25
'yxc': 3.125
}
)
fn init() {