all: support `?Type(none)` (#9567)
							parent
							
								
									3637bac716
								
							
						
					
					
						commit
						6a5f49afb1
					
				| 
						 | 
					@ -1402,7 +1402,6 @@ pub mut:
 | 
				
			||||||
pub struct None {
 | 
					pub struct None {
 | 
				
			||||||
pub:
 | 
					pub:
 | 
				
			||||||
	pos token.Position
 | 
						pos token.Position
 | 
				
			||||||
	foo int // todo
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub enum SqlStmtKind {
 | 
					pub enum SqlStmtKind {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -362,6 +362,9 @@ pub fn (x Expr) str() string {
 | 
				
			||||||
		UnsafeExpr {
 | 
							UnsafeExpr {
 | 
				
			||||||
			return 'unsafe { $x.expr }'
 | 
								return 'unsafe { $x.expr }'
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							None {
 | 
				
			||||||
 | 
								return 'none'
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		else {}
 | 
							else {}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return '[unhandled expr type $x.type_name()]'
 | 
						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
 | 
									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)
 | 
								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 {
 | 
						} 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)
 | 
							c.type_implements(node.expr_type, node.typ, node.pos)
 | 
				
			||||||
	} else if node.typ == ast.bool_type {
 | 
						} else if node.typ == ast.bool_type {
 | 
				
			||||||
		c.error('cannot cast to bool - use e.g. `some_int != 0` instead', node.pos)
 | 
							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)
 | 
							type_name := c.table.type_to_str(node.typ)
 | 
				
			||||||
		c.error('cannot cast `none` to `$type_name`', node.pos)
 | 
							c.error('cannot cast `none` to `$type_name`', node.pos)
 | 
				
			||||||
	} else if from_type_sym.kind == .struct_ && !node.expr_type.is_ptr() {
 | 
						} 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('*')
 | 
								g.write('*')
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if expected_type.has_flag(.optional) && expr is ast.None {
 | 
				
			||||||
 | 
							g.gen_optional_error(expected_type, expr)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	// no cast
 | 
						// no cast
 | 
				
			||||||
	g.expr(expr)
 | 
						g.expr(expr)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2902,61 +2906,7 @@ fn (mut g Gen) expr(node ast.Expr) {
 | 
				
			||||||
			*/
 | 
								*/
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ast.CastExpr {
 | 
							ast.CastExpr {
 | 
				
			||||||
			// g.write('/*cast*/')
 | 
								g.cast_expr(node)
 | 
				
			||||||
			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('))')
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ast.ChanInit {
 | 
							ast.ChanInit {
 | 
				
			||||||
			elem_typ_str := g.typ(node.elem_type)
 | 
								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))
 | 
						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) {
 | 
					fn (mut g Gen) concat_expr(node ast.ConcatExpr) {
 | 
				
			||||||
	styp := g.typ(node.return_type)
 | 
						styp := g.typ(node.return_type)
 | 
				
			||||||
	sym := g.table.get_type_symbol(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) {
 | 
					fn (mut g Gen) return_statement(node ast.Return) {
 | 
				
			||||||
	g.write_v_source_line_info(node.pos)
 | 
						g.write_v_source_line_info(node.pos)
 | 
				
			||||||
	if node.exprs.len > 0 {
 | 
						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
 | 
							optional_none := node.exprs[0] is ast.None
 | 
				
			||||||
		ftyp := g.typ(node.types[0])
 | 
							ftyp := g.typ(node.types[0])
 | 
				
			||||||
		mut is_regular_option := ftyp in ['Option2', 'Option']
 | 
							mut is_regular_option := ftyp in ['Option2', 'Option']
 | 
				
			||||||
		if optional_none || is_regular_option {
 | 
							if optional_none || is_regular_option || node.types[0] == ast.error_type_idx {
 | 
				
			||||||
			styp := g.typ(g.fn_decl.return_type)
 | 
								g.write('return ')
 | 
				
			||||||
			g.write('return ($styp){ .state=2, .err=')
 | 
								g.gen_optional_error(g.fn_decl.return_type, node.exprs[0])
 | 
				
			||||||
			g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type)
 | 
								g.writeln(';')
 | 
				
			||||||
			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(' };')
 | 
					 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1938,14 +1938,16 @@ pub fn (mut p Parser) name_expr() ast.Expr {
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		false
 | 
							false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						is_optional := p.tok.kind == .question
 | 
				
			||||||
	// p.warn('name expr  $p.tok.lit $p.peek_tok.str()')
 | 
						// p.warn('name expr  $p.tok.lit $p.peek_tok.str()')
 | 
				
			||||||
	same_line := p.tok.line_nr == p.peek_tok.line_nr
 | 
						same_line := p.tok.line_nr == p.peek_tok.line_nr
 | 
				
			||||||
	// `(` must be on same line as name token otherwise it's a ParExpr
 | 
						// `(` must be on same line as name token otherwise it's a ParExpr
 | 
				
			||||||
	if !same_line && p.peek_tok.kind == .lpar {
 | 
						if !same_line && p.peek_tok.kind == .lpar {
 | 
				
			||||||
		node = p.parse_ident(language)
 | 
							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
 | 
							// 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 {
 | 
							if mod.len > 0 {
 | 
				
			||||||
			name = '${mod}.$name'
 | 
								name = '${mod}.$name'
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -1992,6 +1994,9 @@ pub fn (mut p Parser) name_expr() ast.Expr {
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			// fn call
 | 
								// fn call
 | 
				
			||||||
			// println('calling $p.tok.lit')
 | 
								// 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)
 | 
								node = p.call_expr(language, mod)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else if (p.peek_tok.kind == .lcbr || (p.peek_tok.kind == .lt && lit0_is_capital))
 | 
						} 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)
 | 
								node = p.parse_ident(ast.Language.v)
 | 
				
			||||||
			p.is_stmt_ident = is_stmt_ident
 | 
								p.is_stmt_ident = is_stmt_ident
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		.name {
 | 
							.name, .question {
 | 
				
			||||||
			if p.tok.lit == 'sql' && p.peek_tok.kind == .name {
 | 
								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
 | 
									p.inside_match = true // reuse the same var for perf instead of inside_sql TODO rename
 | 
				
			||||||
				node = p.sql_expr()
 | 
									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