checker: do not allow nil sum types init

pull/9544/head
Alexander Medvednikov 2021-03-31 11:13:15 +03:00
parent dcab79146b
commit 6f318be96c
34 changed files with 133 additions and 61 deletions

View File

@ -0,0 +1,5 @@
module builtin
struct string {
len int
}

View File

@ -12,25 +12,25 @@ pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
pub type Expr = AnonFn | ArrayDecompose | ArrayInit | AsCast | Assoc | AtExpr | BoolLiteral |
CTempVar | CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall |
ComptimeSelector | ConcatExpr | DumpExpr | EnumVal | FloatLiteral | GoExpr | Ident |
IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr |
ComptimeSelector | ConcatExpr | DumpExpr | EmptyExpr | EnumVal | FloatLiteral | GoExpr |
Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr |
MapInit | MatchExpr | NodeError | None | OffsetOf | OrExpr | ParExpr | PostfixExpr |
PrefixExpr | RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral |
StringLiteral | StructInit | Type | TypeOf | UnsafeExpr
pub type Stmt = AsmStmt | AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl |
DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl |
GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | NodeError |
Return | SqlStmt | StructDecl | TypeDecl
DeferStmt | EmptyStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt |
GlobalDecl | GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module |
NodeError | Return | SqlStmt | StructDecl | TypeDecl
// NB: when you add a new Expr or Stmt type with a .pos field, remember to update
// the .position() token.Position methods too.
pub type ScopeObject = AsmRegister | ConstField | GlobalField | Var
// TOOD: replace table.Param
// TODO: replace table.Param
pub type Node = CallArg | ConstField | EnumField | Expr | Field | File | GlobalField |
IfBranch | MatchBranch | ScopeObject | SelectBranch | Stmt | StructField | StructInitField |
table.Param
IfBranch | MatchBranch | NodeError | ScopeObject | SelectBranch | Stmt | StructField |
StructInitField | table.Param
pub struct Type {
pub:
@ -38,6 +38,21 @@ pub:
pos token.Position
}
pub struct EmptyExpr {}
pub fn empty_expr() Expr {
return EmptyExpr{}
}
pub struct EmptyStmt {
pub:
pos token.Position
}
pub fn empty_stmt() Stmt {
return EmptyStmt{}
}
// `{stmts}` or `unsafe {stmts}`
pub struct Block {
pub:
@ -1431,6 +1446,10 @@ pub fn (expr Expr) position() token.Position {
AnonFn {
return expr.decl.pos
}
EmptyExpr {
println('compiler bug, unhandled EmptyExpr position()')
return token.Position{}
}
NodeError, ArrayDecompose, ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr, CastExpr,
ChanInit, CharLiteral, ConcatExpr, Comment, ComptimeCall, ComptimeSelector, EnumVal, DumpExpr,
FloatLiteral, GoExpr, Ident, IfExpr, IndexExpr, IntegerLiteral, Likely, LockExpr, MapInit,
@ -1540,6 +1559,9 @@ pub:
pub fn (node Node) position() token.Position {
match node {
NodeError {
return token.Position{}
}
Stmt {
mut pos := node.pos
if node is Import {
@ -1736,15 +1758,19 @@ pub fn (node Node) children() []Node {
// a dependency cycle between v.ast and v.table, for the single
// field table.Field.default_expr, which should be ast.Expr
pub fn fe2ex(x table.FExpr) Expr {
res := Expr{}
unsafe { C.memcpy(&res, &x, sizeof(Expr)) }
return res
unsafe {
res := Expr{}
C.memcpy(&res, &x, sizeof(Expr))
return res
}
}
pub fn ex2fe(x Expr) table.FExpr {
res := table.FExpr{}
unsafe { C.memcpy(&res, &x, sizeof(table.FExpr)) }
return res
unsafe {
res := table.FExpr{}
C.memcpy(&res, &x, sizeof(table.FExpr))
return res
}
}
// helper for dealing with `m[k1][k2][k3][k3] = value`

View File

@ -9,9 +9,9 @@ pub fn resolve_init(node StructInit, typ table.Type, t &table.Table) Expr {
mut has_len := false
mut has_cap := false
mut has_default := false
mut len_expr := Expr{}
mut cap_expr := Expr{}
mut default_expr := Expr{}
mut len_expr := empty_expr()
mut cap_expr := empty_expr()
mut default_expr := empty_expr()
mut exprs := []Expr{}
for field in node.fields {
match field.name {

View File

@ -469,6 +469,9 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
utyp := c.unwrap_generic(struct_init.typ)
c.ensure_type_exists(utyp, struct_init.pos) or {}
type_sym := c.table.get_type_symbol(utyp)
if !c.inside_unsafe && type_sym.kind == .sum_type {
c.warn('direct sum type init (`x := SumType{}`) will be removed soon', struct_init.pos)
}
// Make sure the first letter is capital, do not allow e.g. `x := string{}`,
// but `x := T{}` is ok.
if !c.is_builtin_mod && !c.inside_unsafe && type_sym.language == .v
@ -3386,6 +3389,10 @@ fn (mut c Checker) stmt(node ast.Stmt) {
}
// c.expected_type = table.void_type
match mut node {
ast.EmptyStmt {
print_backtrace()
eprintln('Checker.stmt() EmptyStmt')
}
ast.NodeError {}
ast.AsmStmt {
c.asm_stmt(mut node)
@ -3532,9 +3539,13 @@ fn (mut c Checker) branch_stmt(node ast.BranchStmt) {
fn (mut c Checker) for_c_stmt(node ast.ForCStmt) {
c.in_for_count++
prev_loop_label := c.loop_label
c.stmt(node.init)
if node.has_init {
c.stmt(node.init)
}
c.expr(node.cond)
c.stmt(node.inc)
if node.has_inc {
c.stmt(node.inc)
}
c.check_loop_label(node.label, node.pos)
c.stmts(node.stmts)
c.loop_label = prev_loop_label
@ -3975,6 +3986,10 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
}
match mut node {
ast.NodeError {}
ast.EmptyExpr {
print_backtrace()
c.error('checker.expr(): unhandled EmptyExpr', token.Position{})
}
ast.CTempVar {
return node.typ
}
@ -5567,7 +5582,7 @@ fn (mut c Checker) find_obj_definition(obj ast.ScopeObject) ?ast.Expr {
match obj {
ast.Var, ast.ConstField, ast.GlobalField, ast.AsmRegister { name = obj.name }
}
mut expr := ast.Expr{}
mut expr := ast.empty_expr()
if obj is ast.Var {
if obj.is_mut {
return error('`$name` is mut and may have changed since its definition')
@ -6202,7 +6217,9 @@ fn (mut c Checker) sql_stmt(mut node ast.SqlStmt) table.Type {
c.expr(expr)
}
}
c.expr(node.where_expr)
if node.where_expr !is ast.EmptyExpr {
c.expr(node.where_expr)
}
return table.void_type
}

View File

@ -1,6 +1,6 @@
vlib/v/checker/tests/sum_type_assign_non_variant_err.vv:11:6: error: cannot assign to `w`: expected `Stmt`, not `IfExpr`
9 | fn main() {
10 | mut w := Stmt{}
10 | mut w := Stmt(AnotherThing{})
11 | w = IfExpr{}
| ~~~~~~~~
12 | }
12 | }

View File

@ -7,6 +7,6 @@ type Stmt = Expr | AnotherThing
struct AnotherThing {}
fn main() {
mut w := Stmt{}
mut w := Stmt(AnotherThing{})
w = IfExpr{}
}
}

View File

@ -365,7 +365,7 @@ pub fn (mut f Fmt) node_str(node ast.Node) string {
//=== General Stmt-related methods and helpers ===//
pub fn (mut f Fmt) stmts(stmts []ast.Stmt) {
mut prev_stmt := if stmts.len > 0 { stmts[0] } else { ast.Stmt{} }
mut prev_stmt := if stmts.len > 0 { stmts[0] } else { ast.empty_stmt() }
f.indent++
for stmt in stmts {
if !f.pref.building_v && f.should_insert_newline_before_node(stmt, prev_stmt) {
@ -383,6 +383,7 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
}
match node {
ast.NodeError {}
ast.EmptyStmt {}
ast.AsmStmt {
f.asm_stmt(node)
}
@ -482,6 +483,7 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
}
match mut node {
ast.NodeError {}
ast.EmptyExpr {}
ast.AnonFn {
f.fn_decl(node.decl)
}
@ -897,7 +899,11 @@ pub fn (mut f Fmt) const_decl(node ast.ConstDecl) {
}
f.indent++
}
mut prev_field := if node.fields.len > 0 { ast.Node(node.fields[0]) } else { ast.Node{} }
mut prev_field := if node.fields.len > 0 {
ast.Node(node.fields[0])
} else {
ast.Node(ast.NodeError{})
}
for field in node.fields {
if field.comments.len > 0 {
if f.should_insert_newline_before_node(ast.Expr(field.comments[0]), prev_field) {

View File

@ -1010,6 +1010,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
// println('g.stmt()')
// g.writeln('//// stmt start')
match node {
ast.EmptyStmt {}
ast.AsmStmt {
g.write_v_source_line_info(node.pos)
g.gen_asm_stmt(node)
@ -2789,6 +2790,9 @@ fn (mut g Gen) expr(node ast.Expr) {
}
// NB: please keep the type names in the match here in alphabetical order:
match mut node {
ast.EmptyExpr {
g.error('g.expr(): unhandled EmptyExpr', token.Position{})
}
ast.AnonFn {
// TODO: dont fiddle with buffers
g.gen_anon_fn_decl(mut node)
@ -4158,7 +4162,7 @@ fn (mut g Gen) select_expr(node ast.SelectExpr) {
mut is_push := []bool{cap: n_channels}
mut has_else := false
mut has_timeout := false
mut timeout_expr := ast.Expr{}
mut timeout_expr := ast.empty_expr()
mut exception_branch := -1
for j, branch in node.branches {
if branch.is_else {
@ -4182,7 +4186,7 @@ fn (mut g Gen) select_expr(node ast.SelectExpr) {
elem_types << ''
} else {
// must be evaluated to tmp var before real `select` is performed
objs << ast.Expr{}
objs << ast.empty_expr()
tmp_obj := g.new_tmp_var()
tmp_objs << tmp_obj
el_stype := g.typ(g.table.mktyp(expr.right_type))
@ -5283,6 +5287,14 @@ fn (mut g Gen) write_builtin_types() {
for builtin_name in c.builtins {
sym := g.table.type_symbols[g.table.type_idxs[builtin_name]]
if sym.kind == .interface_ {
if g.pref.is_verbose {
println('XAXAXA $sym.name')
if isnil(sym.info) {
println('FFF')
}
println(sym.info)
println(sym.kind)
}
g.write_interface_typesymbol_declaration(sym)
} else {
builtin_types << sym

View File

@ -356,6 +356,7 @@ fn (mut g JsGen) stmts(stmts []ast.Stmt) {
fn (mut g JsGen) stmt(node ast.Stmt) {
g.stmt_start_pos = g.ns.out.len
match node {
ast.EmptyStmt{}
ast.AsmStmt {
panic('inline asm is not supported by js')
}
@ -447,6 +448,7 @@ fn (mut g JsGen) stmt(node ast.Stmt) {
fn (mut g JsGen) expr(node ast.Expr) {
match node {
ast.NodeError {}
ast.EmptyExpr {}
ast.CTempVar {
g.write('/* ast.CTempVar: node.name */')
}

View File

@ -49,6 +49,7 @@ pub fn (mut w Walker) mark_root_fns(all_fn_root_names []string) {
pub fn (mut w Walker) stmt(node ast.Stmt) {
match mut node {
ast.EmptyStmt {}
ast.AsmStmt {
w.asm_io(node.output)
w.asm_io(node.input)
@ -153,6 +154,9 @@ fn (mut w Walker) exprs(exprs []ast.Expr) {
fn (mut w Walker) expr(node ast.Expr) {
match mut node {
ast.EmptyExpr {
panic('Walker: EmptyExpr')
}
ast.AnonFn {
w.fn_decl(mut node.decl)
}

View File

@ -149,7 +149,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
r0 := right[0]
mut v := ast.Var{
name: lx.name
expr: if left.len == right.len { right[i] } else { ast.Expr{} }
expr: if left.len == right.len { right[i] } else { ast.empty_expr() }
share: share
is_mut: lx.is_mut || p.inside_for
pos: lx.pos

View File

@ -21,7 +21,7 @@ fn (mut p Parser) array_init() ast.ArrayInit {
mut has_val := false
mut has_type := false
mut has_default := false
mut default_expr := ast.Expr{}
mut default_expr := ast.empty_expr()
if p.tok.kind == .rsbr {
last_pos = p.tok.position()
// []typ => `[]` and `typ` must be on the same line
@ -103,8 +103,8 @@ fn (mut p Parser) array_init() ast.ArrayInit {
}
mut has_len := false
mut has_cap := false
mut len_expr := ast.Expr{}
mut cap_expr := ast.Expr{}
mut len_expr := ast.empty_expr()
mut cap_expr := ast.empty_expr()
if p.tok.kind == .lcbr && exprs.len == 0 && array_type != table.void_type {
// `[]int{ len: 10, cap: 100}` syntax
p.next()

View File

@ -129,7 +129,7 @@ pub fn (mut p Parser) call_args() []ast.CallArg {
p.next()
array_decompose = true
}
mut expr := ast.Expr{}
mut expr := ast.empty_expr()
if p.tok.kind == .name && p.peek_tok.kind == .colon {
// `foo(key:val, key2:val2)`
expr = p.struct_init(true) // short_syntax:true

View File

@ -35,9 +35,9 @@ fn (mut p Parser) for_stmt() ast.Stmt {
if p.tok.kind == .key_mut {
return p.error('`mut` is not needed in `for ;;` loops: use `for i := 0; i < n; i ++ {`')
}
mut init := ast.Stmt{}
mut init := ast.empty_stmt()
mut cond := p.new_true_expr()
mut inc := ast.Stmt{}
mut inc := ast.empty_stmt()
mut has_init := false
mut has_cond := false
mut has_inc := false
@ -136,7 +136,7 @@ fn (mut p Parser) for_stmt() ast.Stmt {
// 0 .. 10
// start := p.tok.lit.int()
// TODO use RangeExpr
mut high_expr := ast.Expr{}
mut high_expr := ast.empty_expr()
mut is_range := false
if p.tok.kind == .dotdot {
is_range = true

View File

@ -79,7 +79,7 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr {
return ast.IfExpr{}
}
comments << p.eat_comments({})
mut cond := ast.Expr{}
mut cond := ast.empty_expr()
mut is_guard := false
// `if x := opt() {`
if !is_comptime && p.peek_tok.kind == .decl_assign {
@ -290,7 +290,7 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
// final else
mut is_else := false
mut is_timeout := false
mut stmt := ast.Stmt{}
mut stmt := ast.empty_stmt()
if p.tok.kind == .key_else {
if has_timeout {
p.error_with_pos('timeout `> t` and `else` are mutually exclusive `select` keys',

View File

@ -597,7 +597,7 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
}
// TODO remove dummy return statement
// the compiler complains if it's not there
return ast.Stmt{}
return ast.empty_stmt()
}
// TODO [if vfmt]
@ -1757,7 +1757,7 @@ fn (p &Parser) is_generic_call() bool {
pub fn (mut p Parser) name_expr() ast.Expr {
prev_tok_kind := p.prev_tok.kind
mut node := ast.Expr{}
mut node := ast.empty_expr()
if p.expecting_type {
p.expecting_type = false
// get type position before moving to next
@ -1801,7 +1801,7 @@ pub fn (mut p Parser) name_expr() ast.Expr {
mut last_pos := first_pos
chan_type := p.parse_chan_type()
mut has_cap := false
mut cap_expr := ast.Expr{}
mut cap_expr := ast.empty_expr()
p.check(.lcbr)
if p.tok.kind == .rcbr {
last_pos = p.tok.position()
@ -1910,8 +1910,8 @@ pub fn (mut p Parser) name_expr() ast.Expr {
// without the next line int would result in int*
p.is_amp = false
p.check(.lpar)
mut expr := ast.Expr{}
mut arg := ast.Expr{}
mut expr := ast.empty_expr()
mut arg := ast.empty_expr()
mut has_arg := false
expr = p.expr(0)
// TODO, string(b, len)
@ -2013,7 +2013,7 @@ fn (mut p Parser) index_expr(left ast.Expr) ast.IndexExpr {
left: left
pos: pos
index: ast.RangeExpr{
low: ast.Expr{}
low: ast.empty_expr()
high: high
has_high: true
pos: pos
@ -2025,7 +2025,7 @@ fn (mut p Parser) index_expr(left ast.Expr) ast.IndexExpr {
if p.tok.kind == .dotdot {
// [start..end] or [start..]
p.next()
mut high := ast.Expr{}
mut high := ast.empty_expr()
if p.tok.kind != .rsbr {
has_high = true
high = p.expr(0)
@ -2263,7 +2263,7 @@ fn (mut p Parser) string_expr() ast.Expr {
if is_raw || is_cstr {
p.next()
}
mut node := ast.Expr{}
mut node := ast.empty_expr()
val := p.tok.lit
pos := p.tok.position()
if p.peek_tok.kind != .str_dollar {
@ -2372,7 +2372,7 @@ fn (mut p Parser) parse_number_literal() ast.Expr {
}
lit := p.tok.lit
full_lit := if is_neg { '-' + lit } else { lit }
mut node := ast.Expr{}
mut node := ast.empty_expr()
if lit.index_any('.eE') >= 0 && lit[..2] !in ['0x', '0X', '0o', '0O', '0b', '0B'] {
node = ast.FloatLiteral{
val: full_lit
@ -2714,7 +2714,7 @@ fn (mut p Parser) global_decl() ast.GlobalDecl {
p.error('global assign must have the type around the value, use `__global ( name = type(value) )`')
return ast.GlobalDecl{}
}
mut expr := ast.Expr{}
mut expr := ast.empty_expr()
if has_expr {
if p.tok.kind != .lpar {
p.error('global assign must have a type and value, use `__global ( name = type(value) )` or `__global ( name type )`')
@ -2769,7 +2769,7 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
pos := p.tok.position()
val := p.check_name()
vals << val
mut expr := ast.Expr{}
mut expr := ast.empty_expr()
mut has_expr := false
// p.warn('enum val $val')
if p.tok.kind == .assign {
@ -2854,7 +2854,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
if name.len == 1 && name[0].is_capital() {
p.error_with_pos('single letter capital names are reserved for generic template types.',
decl_pos)
return ast.TypeDecl{}
return ast.FnTypeDecl{}
}
mut sum_variants := []ast.SumTypeVariant{}
p.check(.assign)

View File

@ -14,7 +14,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
eprintln('parsing file: ${p.file_name:-30} | tok.kind: ${p.tok.kind:-10} | tok.lit: ${p.tok.lit:-10} | tok_pos: ${tok_pos.str():-45} | expr($precedence)')
}
// println('\n\nparser.expr()')
mut node := ast.Expr{}
mut node := ast.empty_expr()
is_stmt_ident := p.is_stmt_ident
p.is_stmt_ident = false
if !p.pref.is_fmt {
@ -439,7 +439,7 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr {
precedence := p.tok.precedence()
mut pos := p.tok.position()
p.next()
mut right := ast.Expr{}
mut right := ast.empty_expr()
prev_expecting_type := p.expecting_type
if op in [.key_is, .not_is] {
p.expecting_type = true

View File

@ -22,7 +22,7 @@ fn (mut p Parser) sql_expr() ast.Expr {
}
table_pos := p.tok.position()
table_type := p.parse_type() // `User`
mut where_expr := ast.Expr{}
mut where_expr := ast.empty_expr()
has_where := p.tok.kind == .name && p.tok.lit == 'where'
mut query_one := false // one object is returned, not an array
if has_where {
@ -40,11 +40,11 @@ fn (mut p Parser) sql_expr() ast.Expr {
}
}
mut has_limit := false
mut limit_expr := ast.Expr{}
mut limit_expr := ast.empty_expr()
mut has_offset := false
mut offset_expr := ast.Expr{}
mut offset_expr := ast.empty_expr()
mut has_order := false
mut order_expr := ast.Expr{}
mut order_expr := ast.empty_expr()
mut has_desc := false
if p.tok.kind == .name && p.tok.lit == 'order' {
p.check_name() // `order`
@ -172,7 +172,7 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt {
}
mut table_pos := p.tok.position()
mut where_expr := ast.Expr{}
mut where_expr := ast.empty_expr()
if kind == .insert {
table_pos = p.tok.position()
table_type = p.parse_type()

View File

@ -239,7 +239,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
// attrs are stored in `p.attrs`
p.attributes()
}
mut default_expr := ast.Expr{}
mut default_expr := ast.empty_expr()
mut has_default_expr := false
if !is_embed {
if p.tok.kind == .assign {
@ -352,12 +352,12 @@ fn (mut p Parser) struct_init(short_syntax bool) ast.StructInit {
// p.warn(is_short_syntax.str())
saved_is_amp := p.is_amp
p.is_amp = false
mut update_expr := ast.Expr{}
mut update_expr := ast.empty_expr()
mut update_expr_comments := []ast.Comment{}
mut has_update_expr := false
for p.tok.kind !in [.rcbr, .rpar, .eof] {
mut field_name := ''
mut expr := ast.Expr{}
mut expr := ast.empty_expr()
mut field_pos := token.Position{}
mut first_field_pos := token.Position{}
mut comments := []ast.Comment{}