all: support `?Type(none)` (#9567)
							parent
							
								
									3637bac716
								
							
						
					
					
						commit
						6a5f49afb1
					
				| 
						 | 
				
			
			@ -1402,7 +1402,6 @@ pub mut:
 | 
			
		|||
pub struct None {
 | 
			
		||||
pub:
 | 
			
		||||
	pos token.Position
 | 
			
		||||
	foo int // todo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum SqlStmtKind {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -362,6 +362,9 @@ pub fn (x Expr) str() string {
 | 
			
		|||
		UnsafeExpr {
 | 
			
		||||
			return 'unsafe { $x.expr }'
 | 
			
		||||
		}
 | 
			
		||||
		None {
 | 
			
		||||
			return 'none'
 | 
			
		||||
		}
 | 
			
		||||
		else {}
 | 
			
		||||
	}
 | 
			
		||||
	return '[unhandled expr type $x.type_name()]'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4351,7 +4351,7 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
 | 
			
		|||
				ast.f64_type
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		if !c.table.sumtype_has_variant(node.typ, node.expr_type) {
 | 
			
		||||
		if !c.table.sumtype_has_variant(node.typ, node.expr_type) && !node.typ.has_flag(.optional) {
 | 
			
		||||
			c.error('cannot cast `$from_type_sym.name` to `$to_type_sym.name`', node.pos)
 | 
			
		||||
		}
 | 
			
		||||
	} else if mut to_type_sym.info is ast.Alias {
 | 
			
		||||
| 
						 | 
				
			
			@ -4400,7 +4400,7 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
 | 
			
		|||
		c.type_implements(node.expr_type, node.typ, node.pos)
 | 
			
		||||
	} else if node.typ == ast.bool_type {
 | 
			
		||||
		c.error('cannot cast to bool - use e.g. `some_int != 0` instead', node.pos)
 | 
			
		||||
	} else if node.expr_type == ast.none_type {
 | 
			
		||||
	} else if node.expr_type == ast.none_type && !node.typ.has_flag(.optional) {
 | 
			
		||||
		type_name := c.table.type_to_str(node.typ)
 | 
			
		||||
		c.error('cannot cast `none` to `$type_name`', node.pos)
 | 
			
		||||
	} else if from_type_sym.kind == .struct_ && !node.expr_type.is_ptr() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1761,6 +1761,10 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
 | 
			
		|||
			g.write('*')
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if expected_type.has_flag(.optional) && expr is ast.None {
 | 
			
		||||
		g.gen_optional_error(expected_type, expr)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// no cast
 | 
			
		||||
	g.expr(expr)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2902,61 +2906,7 @@ fn (mut g Gen) expr(node ast.Expr) {
 | 
			
		|||
			*/
 | 
			
		||||
		}
 | 
			
		||||
		ast.CastExpr {
 | 
			
		||||
			// g.write('/*cast*/')
 | 
			
		||||
			if g.is_amp {
 | 
			
		||||
				// &Foo(0) => ((Foo*)0)
 | 
			
		||||
				g.out.go_back(1)
 | 
			
		||||
			}
 | 
			
		||||
			g.is_amp = false
 | 
			
		||||
			sym := g.table.get_type_symbol(node.typ)
 | 
			
		||||
			if sym.kind == .string && !node.typ.is_ptr() {
 | 
			
		||||
				// `string(x)` needs `tos()`, but not `&string(x)
 | 
			
		||||
				// `tos(str, len)`, `tos2(str)`
 | 
			
		||||
				if node.has_arg {
 | 
			
		||||
					g.write('tos((byteptr)')
 | 
			
		||||
				} else {
 | 
			
		||||
					g.write('tos2((byteptr)')
 | 
			
		||||
				}
 | 
			
		||||
				g.expr(node.expr)
 | 
			
		||||
				expr_sym := g.table.get_type_symbol(node.expr_type)
 | 
			
		||||
				if expr_sym.kind == .array {
 | 
			
		||||
					// if we are casting an array, we need to add `.data`
 | 
			
		||||
					g.write('.data')
 | 
			
		||||
				}
 | 
			
		||||
				if node.has_arg {
 | 
			
		||||
					// len argument
 | 
			
		||||
					g.write(', ')
 | 
			
		||||
					g.expr(node.arg)
 | 
			
		||||
				}
 | 
			
		||||
				g.write(')')
 | 
			
		||||
			} else if sym.kind in [.sum_type, .interface_] {
 | 
			
		||||
				g.expr_with_cast(node.expr, node.expr_type, node.typ)
 | 
			
		||||
			} else if sym.kind == .struct_ && !node.typ.is_ptr()
 | 
			
		||||
				&& !(sym.info as ast.Struct).is_typedef {
 | 
			
		||||
				// deprecated, replaced by Struct{...exr}
 | 
			
		||||
				styp := g.typ(node.typ)
 | 
			
		||||
				g.write('*(($styp *)(&')
 | 
			
		||||
				g.expr(node.expr)
 | 
			
		||||
				g.write('))')
 | 
			
		||||
			} else {
 | 
			
		||||
				styp := g.typ(node.typ)
 | 
			
		||||
				mut cast_label := ''
 | 
			
		||||
				// `ast.string_type` is done for MSVC's bug
 | 
			
		||||
				if sym.kind != .alias
 | 
			
		||||
					|| (sym.info as ast.Alias).parent_type !in [node.expr_type, ast.string_type] {
 | 
			
		||||
					cast_label = '($styp)'
 | 
			
		||||
				}
 | 
			
		||||
				g.write('(${cast_label}(')
 | 
			
		||||
				g.expr(node.expr)
 | 
			
		||||
				if node.expr is ast.IntegerLiteral {
 | 
			
		||||
					if node.typ in [ast.u64_type, ast.u32_type, ast.u16_type] {
 | 
			
		||||
						if !node.expr.val.starts_with('-') {
 | 
			
		||||
							g.write('U')
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				g.write('))')
 | 
			
		||||
			}
 | 
			
		||||
			g.cast_expr(node)
 | 
			
		||||
		}
 | 
			
		||||
		ast.ChanInit {
 | 
			
		||||
			elem_typ_str := g.typ(node.elem_type)
 | 
			
		||||
| 
						 | 
				
			
			@ -4403,6 +4353,66 @@ fn (mut g Gen) ident(node ast.Ident) {
 | 
			
		|||
	g.write(g.get_ternary_name(name))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (mut g Gen) cast_expr(node ast.CastExpr) {
 | 
			
		||||
	if g.is_amp {
 | 
			
		||||
		// &Foo(0) => ((Foo*)0)
 | 
			
		||||
		g.out.go_back(1)
 | 
			
		||||
	}
 | 
			
		||||
	g.is_amp = false
 | 
			
		||||
	sym := g.table.get_type_symbol(node.typ)
 | 
			
		||||
	if sym.kind == .string && !node.typ.is_ptr() {
 | 
			
		||||
		// `string(x)` needs `tos()`, but not `&string(x)
 | 
			
		||||
		// `tos(str, len)`, `tos2(str)`
 | 
			
		||||
		if node.has_arg {
 | 
			
		||||
			g.write('tos((byteptr)')
 | 
			
		||||
		} else {
 | 
			
		||||
			g.write('tos2((byteptr)')
 | 
			
		||||
		}
 | 
			
		||||
		g.expr(node.expr)
 | 
			
		||||
		expr_sym := g.table.get_type_symbol(node.expr_type)
 | 
			
		||||
		if expr_sym.kind == .array {
 | 
			
		||||
			// if we are casting an array, we need to add `.data`
 | 
			
		||||
			g.write('.data')
 | 
			
		||||
		}
 | 
			
		||||
		if node.has_arg {
 | 
			
		||||
			// len argument
 | 
			
		||||
			g.write(', ')
 | 
			
		||||
			g.expr(node.arg)
 | 
			
		||||
		}
 | 
			
		||||
		g.write(')')
 | 
			
		||||
	} else if sym.kind in [.sum_type, .interface_] {
 | 
			
		||||
		g.expr_with_cast(node.expr, node.expr_type, node.typ)
 | 
			
		||||
	} else if sym.kind == .struct_ && !node.typ.is_ptr() && !(sym.info as ast.Struct).is_typedef {
 | 
			
		||||
		// deprecated, replaced by Struct{...exr}
 | 
			
		||||
		styp := g.typ(node.typ)
 | 
			
		||||
		g.write('*(($styp *)(&')
 | 
			
		||||
		g.expr(node.expr)
 | 
			
		||||
		g.write('))')
 | 
			
		||||
	} else {
 | 
			
		||||
		styp := g.typ(node.typ)
 | 
			
		||||
		mut cast_label := ''
 | 
			
		||||
		// `ast.string_type` is done for MSVC's bug
 | 
			
		||||
		if sym.kind != .alias
 | 
			
		||||
			|| (sym.info as ast.Alias).parent_type !in [node.expr_type, ast.string_type] {
 | 
			
		||||
			cast_label = '($styp)'
 | 
			
		||||
		}
 | 
			
		||||
		if node.typ.has_flag(.optional) && node.expr is ast.None {
 | 
			
		||||
			g.gen_optional_error(node.typ, node.expr)
 | 
			
		||||
		} else {
 | 
			
		||||
			g.write('(${cast_label}(')
 | 
			
		||||
			g.expr(node.expr)
 | 
			
		||||
			if node.expr is ast.IntegerLiteral {
 | 
			
		||||
				if node.typ in [ast.u64_type, ast.u32_type, ast.u16_type] {
 | 
			
		||||
					if !node.expr.val.starts_with('-') {
 | 
			
		||||
						g.write('U')
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			g.write('))')
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (mut g Gen) concat_expr(node ast.ConcatExpr) {
 | 
			
		||||
	styp := g.typ(node.return_type)
 | 
			
		||||
	sym := g.table.get_type_symbol(node.return_type)
 | 
			
		||||
| 
						 | 
				
			
			@ -4598,6 +4608,13 @@ fn (g &Gen) expr_is_multi_return_call(expr ast.Expr) bool {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (mut g Gen) gen_optional_error(target_type ast.Type, expr ast.Expr) {
 | 
			
		||||
	styp := g.typ(target_type)
 | 
			
		||||
	g.write('($styp){ .state=2, .err=')
 | 
			
		||||
	g.expr(expr)
 | 
			
		||||
	g.write(' }')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (mut g Gen) return_statement(node ast.Return) {
 | 
			
		||||
	g.write_v_source_line_info(node.pos)
 | 
			
		||||
	if node.exprs.len > 0 {
 | 
			
		||||
| 
						 | 
				
			
			@ -4635,18 +4652,10 @@ fn (mut g Gen) return_statement(node ast.Return) {
 | 
			
		|||
		optional_none := node.exprs[0] is ast.None
 | 
			
		||||
		ftyp := g.typ(node.types[0])
 | 
			
		||||
		mut is_regular_option := ftyp in ['Option2', 'Option']
 | 
			
		||||
		if optional_none || is_regular_option {
 | 
			
		||||
			styp := g.typ(g.fn_decl.return_type)
 | 
			
		||||
			g.write('return ($styp){ .state=2, .err=')
 | 
			
		||||
			g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type)
 | 
			
		||||
			g.writeln(' };')
 | 
			
		||||
			return
 | 
			
		||||
		} else if node.types[0] == ast.error_type_idx {
 | 
			
		||||
			// foo() or { return err }
 | 
			
		||||
			styp := g.typ(g.fn_decl.return_type)
 | 
			
		||||
			g.write('return ($styp){.state=2, .err=')
 | 
			
		||||
			g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type)
 | 
			
		||||
			g.writeln(' };')
 | 
			
		||||
		if optional_none || is_regular_option || node.types[0] == ast.error_type_idx {
 | 
			
		||||
			g.write('return ')
 | 
			
		||||
			g.gen_optional_error(g.fn_decl.return_type, node.exprs[0])
 | 
			
		||||
			g.writeln(';')
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1938,14 +1938,16 @@ pub fn (mut p Parser) name_expr() ast.Expr {
 | 
			
		|||
	} else {
 | 
			
		||||
		false
 | 
			
		||||
	}
 | 
			
		||||
	is_optional := p.tok.kind == .question
 | 
			
		||||
	// p.warn('name expr  $p.tok.lit $p.peek_tok.str()')
 | 
			
		||||
	same_line := p.tok.line_nr == p.peek_tok.line_nr
 | 
			
		||||
	// `(` must be on same line as name token otherwise it's a ParExpr
 | 
			
		||||
	if !same_line && p.peek_tok.kind == .lpar {
 | 
			
		||||
		node = p.parse_ident(language)
 | 
			
		||||
	} else if p.peek_tok.kind == .lpar || p.is_generic_call() {
 | 
			
		||||
	} else if p.peek_tok.kind == .lpar
 | 
			
		||||
		|| (is_optional && p.peek_token(2).kind == .lpar) || p.is_generic_call() {
 | 
			
		||||
		// foo(), foo<int>() or type() cast
 | 
			
		||||
		mut name := p.tok.lit
 | 
			
		||||
		mut name := if is_optional { p.peek_tok.lit } else { p.tok.lit }
 | 
			
		||||
		if mod.len > 0 {
 | 
			
		||||
			name = '${mod}.$name'
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -1992,6 +1994,9 @@ pub fn (mut p Parser) name_expr() ast.Expr {
 | 
			
		|||
		} else {
 | 
			
		||||
			// fn call
 | 
			
		||||
			// println('calling $p.tok.lit')
 | 
			
		||||
			if is_optional {
 | 
			
		||||
				p.error_with_pos('unexpected $p.prev_tok', p.prev_tok.position())
 | 
			
		||||
			}
 | 
			
		||||
			node = p.call_expr(language, mod)
 | 
			
		||||
		}
 | 
			
		||||
	} else if (p.peek_tok.kind == .lcbr || (p.peek_tok.kind == .lt && lit0_is_capital))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
 | 
			
		|||
			node = p.parse_ident(ast.Language.v)
 | 
			
		||||
			p.is_stmt_ident = is_stmt_ident
 | 
			
		||||
		}
 | 
			
		||||
		.name {
 | 
			
		||||
		.name, .question {
 | 
			
		||||
			if p.tok.lit == 'sql' && p.peek_tok.kind == .name {
 | 
			
		||||
				p.inside_match = true // reuse the same var for perf instead of inside_sql TODO rename
 | 
			
		||||
				node = p.sql_expr()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,25 @@
 | 
			
		|||
type Foo = int | string
 | 
			
		||||
 | 
			
		||||
fn test_optional_none_assign() {
 | 
			
		||||
	x := ?Foo(none)
 | 
			
		||||
	// assert x.err == none
 | 
			
		||||
	// assert !x.has_value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_optional_none_assign_nonsumtype() {
 | 
			
		||||
	x := ?int(none)
 | 
			
		||||
	// assert x.err == none
 | 
			
		||||
	// assert !x.has_value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: make this working next
 | 
			
		||||
/*
 | 
			
		||||
fn test_optional_value_assign() {
 | 
			
		||||
	x := ?Foo('test')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_optional_none_reassign() {
 | 
			
		||||
	mut x := ?Foo('test')
 | 
			
		||||
	x = none
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
		Loading…
	
		Reference in New Issue