checker: disallow `opt_returning_string() or { ... 123 }` (closes #6711)
							parent
							
								
									d040af4939
								
							
						
					
					
						commit
						4e760c703e
					
				| 
						 | 
					@ -1609,7 +1609,7 @@ pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type table.Type) t
 | 
				
			||||||
						expr.pos)
 | 
											expr.pos)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				c.check_or_expr(expr.or_block, ret_type)
 | 
									c.check_or_expr(expr.or_block, ret_type, expr.return_type.clear_flag(.optional))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			// remove optional flag
 | 
								// remove optional flag
 | 
				
			||||||
			// return ret_type.clear_flag(.optional)
 | 
								// return ret_type.clear_flag(.optional)
 | 
				
			||||||
| 
						 | 
					@ -1626,7 +1626,7 @@ pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type table.Type) t
 | 
				
			||||||
	return ret_type
 | 
						return ret_type
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn (mut c Checker) check_or_expr(or_expr ast.OrExpr, ret_type table.Type) {
 | 
					pub fn (mut c Checker) check_or_expr(or_expr ast.OrExpr, ret_type table.Type, expr_return_type table.Type) {
 | 
				
			||||||
	if or_expr.kind == .propagate {
 | 
						if or_expr.kind == .propagate {
 | 
				
			||||||
		if !c.cur_fn.return_type.has_flag(.optional) && c.cur_fn.name != 'main.main' {
 | 
							if !c.cur_fn.return_type.has_flag(.optional) && c.cur_fn.name != 'main.main' {
 | 
				
			||||||
			c.error('to propagate the optional call, `$c.cur_fn.name` must itself return an optional',
 | 
								c.error('to propagate the optional call, `$c.cur_fn.name` must itself return an optional',
 | 
				
			||||||
| 
						 | 
					@ -1646,10 +1646,10 @@ pub fn (mut c Checker) check_or_expr(or_expr ast.OrExpr, ret_type table.Type) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	last_stmt := or_expr.stmts[stmts_len - 1]
 | 
						last_stmt := or_expr.stmts[stmts_len - 1]
 | 
				
			||||||
	if ret_type != table.void_type {
 | 
						if ret_type != table.void_type {
 | 
				
			||||||
		match mut last_stmt {
 | 
							match last_stmt {
 | 
				
			||||||
			ast.ExprStmt {
 | 
								ast.ExprStmt {
 | 
				
			||||||
				last_stmt.typ = c.expr(last_stmt.expr)
 | 
									last_stmt_typ := c.expr(last_stmt.expr)
 | 
				
			||||||
				type_fits := c.check_types(last_stmt.typ, ret_type)
 | 
									type_fits := c.check_types(last_stmt_typ, ret_type)
 | 
				
			||||||
				is_panic_or_exit := is_expr_panic_or_exit(last_stmt.expr)
 | 
									is_panic_or_exit := is_expr_panic_or_exit(last_stmt.expr)
 | 
				
			||||||
				if type_fits || is_panic_or_exit {
 | 
									if type_fits || is_panic_or_exit {
 | 
				
			||||||
					return
 | 
										return
 | 
				
			||||||
| 
						 | 
					@ -1659,7 +1659,7 @@ pub fn (mut c Checker) check_or_expr(or_expr ast.OrExpr, ret_type table.Type) {
 | 
				
			||||||
					c.error('`or` block must provide a default value of type `$expected_type_name`, or return/exit/continue/break/panic',
 | 
										c.error('`or` block must provide a default value of type `$expected_type_name`, or return/exit/continue/break/panic',
 | 
				
			||||||
						last_stmt.pos)
 | 
											last_stmt.pos)
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					type_name := c.table.type_to_str(last_stmt.typ)
 | 
										type_name := c.table.type_to_str(last_stmt_typ)
 | 
				
			||||||
					c.error('wrong return type `$type_name` in the `or {}` block, expected `$expected_type_name`',
 | 
										c.error('wrong return type `$type_name` in the `or {}` block, expected `$expected_type_name`',
 | 
				
			||||||
						last_stmt.pos)
 | 
											last_stmt.pos)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
| 
						 | 
					@ -1680,6 +1680,26 @@ pub fn (mut c Checker) check_or_expr(or_expr ast.OrExpr, ret_type table.Type) {
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							match last_stmt {
 | 
				
			||||||
 | 
								ast.ExprStmt {
 | 
				
			||||||
 | 
									if last_stmt.typ == table.void_type {
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if is_expr_panic_or_exit(last_stmt.expr) {
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if c.check_types(last_stmt.typ, expr_return_type) {
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									// opt_returning_string() or { ... 123 }
 | 
				
			||||||
 | 
									type_name := c.table.type_to_str(last_stmt.typ)
 | 
				
			||||||
 | 
									expr_return_type_name := c.table.type_to_str(expr_return_type)
 | 
				
			||||||
 | 
									c.error('the default expression type in the `or` block should be `$expr_return_type_name`, instead you gave a value of type `$type_name`',
 | 
				
			||||||
 | 
										last_stmt.expr.position())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								else {}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					vlib/v/checker/tests/optional_or_block_returns_value_of_incompatible_type.vv:13:3: error: the default expression type in the `or` block should be `string`, instead you gave a value of type `any_int`
 | 
				
			||||||
 | 
					   11 |         // must be of the same type of the return
 | 
				
			||||||
 | 
					   12 |         // type of the `test_optional` function
 | 
				
			||||||
 | 
					   13 |         123
 | 
				
			||||||
 | 
					      |         ~~~
 | 
				
			||||||
 | 
					   14 |         // 'I break things'
 | 
				
			||||||
 | 
					   15 |     }
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					fn test_optional(fail bool) ?string {
 | 
				
			||||||
 | 
						if fail {
 | 
				
			||||||
 | 
							return error('false')
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 'fff'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
						// a := test_optional(false) or { println(err) }
 | 
				
			||||||
 | 
						test_optional(true) or {
 | 
				
			||||||
 | 
							// must be of the same type of the return
 | 
				
			||||||
 | 
							// type of the `test_optional` function
 | 
				
			||||||
 | 
							123
 | 
				
			||||||
 | 
							// 'I break things'
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue