cgen: print results of failed callexprs in `assert fn() == fn2()` (#6665)

pull/6666/head
Delyan Angelov 2020-10-21 22:58:40 +03:00 committed by GitHub
parent 6d11caf784
commit ab137e4164
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 77 additions and 9 deletions

View File

@ -9,8 +9,8 @@ import v.errors
pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | CastExpr |
ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral |
pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CTempVar | CallExpr |
CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral |
Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr |
MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr |
SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | StructInit |
@ -439,9 +439,9 @@ pub struct InfixExpr {
pub:
op token.Kind
pos token.Position
pub mut:
left Expr
right Expr
pub mut:
left_type table.Type
right_type table.Type
auto_locked string
@ -826,8 +826,9 @@ pub mut:
pub struct AssertStmt {
pub:
expr Expr
pos token.Position
pub mut:
expr Expr
}
// `if [x := opt()] {`
@ -1025,6 +1026,7 @@ pub fn (expr Expr) position() token.Position {
pub fn (expr Expr) is_lvalue() bool {
match expr {
Ident { return true }
CTempVar { return true }
IndexExpr { return expr.left.is_lvalue() }
SelectorExpr { return expr.expr.is_lvalue() }
else {}
@ -1058,6 +1060,15 @@ pub fn (stmt Stmt) check_c_expr() ? {
return error('unsupported statement (`${typeof(stmt)}`)')
}
// CTempVar is used in cgen only, to hold nodes for temporary variables
pub struct CTempVar {
pub:
name string // the name of the C temporary variable; used by g.expr(x)
orig Expr // the original expression, which produced the C temp variable; used by x.str()
typ table.Type // the type of the original expression
is_ptr bool // whether the type is a pointer
}
pub fn (stmt Stmt) position() token.Position {
match stmt {
AssertStmt, AssignStmt, Block, ConstDecl, EnumDecl, ExprStmt, FnDecl, ForCStmt, ForInStmt, ForStmt, Import, Return, StructDecl { return stmt.pos }

View File

@ -177,6 +177,9 @@ pub fn (lit &StringInterLiteral) get_fspec_braces(i int) (string, bool) {
// string representation of expr
pub fn (x Expr) str() string {
match x {
CTempVar {
return x.orig.str()
}
BoolLiteral {
return x.val.str()
}
@ -188,6 +191,9 @@ pub fn (x Expr) str() string {
if x.is_method {
return '${x.left.str()}.${x.name}($sargs)'
}
if x.name.starts_with('${x.mod}.') {
return util.strip_main_name('${x.name}($sargs)')
}
return '${x.mod}.${x.name}($sargs)'
}
CharLiteral {

View File

@ -2563,6 +2563,9 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
return table.void_type
}
match mut node {
ast.CTempVar {
return node.typ
}
ast.AnonFn {
keep_fn := c.cur_fn
c.cur_fn = &node.decl

View File

@ -711,6 +711,9 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
eprintln('expr: ${node.position():-42} | node: ${typeof(node):-20} | $node.str()')
}
match mut node {
ast.CTempVar {
eprintln('ast.CTempVar of $node.orig.str() should be generated/used only in cgen')
}
ast.AnonFn {
f.fn_decl(node.decl)
}

View File

@ -1272,8 +1272,40 @@ fn (mut g Gen) gen_attrs(attrs []table.Attr) {
}
}
fn (mut g Gen) gen_assert_stmt(a ast.AssertStmt) {
fn (mut g Gen) gen_assert_stmt(original_assert_statement ast.AssertStmt) {
mut a := original_assert_statement
g.writeln('// assert')
mut tl_name := ''
mut tr_name := ''
if a.expr is ast.InfixExpr {
mut aie := a.expr as ast.InfixExpr
if aie.left is ast.CallExpr {
tl_styp := g.typ(aie.left_type)
tl_name = g.new_tmp_var()
g.write('$tl_styp $tl_name = ')
g.expr(aie.left)
g.writeln(';')
aie.left = ast.Expr(ast.CTempVar{
name: tl_name
typ: aie.left_type
is_ptr: aie.left_type.is_ptr()
orig: aie.left
})
}
if aie.right is ast.CallExpr {
tr_styp := g.typ(aie.right_type)
tr_name = g.new_tmp_var()
g.write('$tr_styp $tr_name = ')
g.expr(aie.right)
g.writeln(';')
aie.right = ast.Expr(ast.CTempVar{
name: tr_name
typ: aie.right_type
is_ptr: aie.right_type.is_ptr()
orig: aie.right
})
}
}
g.inside_ternary++
g.write('if (')
g.expr(a.expr)
@ -1338,7 +1370,7 @@ fn (mut g Gen) gen_assert_metainfo(a ast.AssertStmt) string {
fn (mut g Gen) gen_assert_single_expr(e ast.Expr, t table.Type) {
unknown_value := '*unknown value*'
match e {
ast.CallExpr, ast.CastExpr, ast.IndexExpr, ast.PrefixExpr, ast.MatchExpr {
ast.CastExpr, ast.IndexExpr, ast.PrefixExpr, ast.MatchExpr {
g.write(ctoslit(unknown_value))
}
ast.Type {
@ -1962,6 +1994,7 @@ fn (mut g Gen) gen_anon_fn_decl(it ast.AnonFn) {
fn (mut g Gen) expr(node ast.Expr) {
// println('cgen expr() line_nr=$node.pos.line_nr')
// NB: please keep the type names in the match here in alphabetical order:
match node {
ast.AnonFn {
// TODO: dont fiddle with buffers
@ -2084,6 +2117,10 @@ fn (mut g Gen) expr(node ast.Expr) {
ast.ConcatExpr {
g.concat_expr(node)
}
ast.CTempVar {
// g.write('/*ctmp .orig: $node.orig.str() , .typ: $node.typ, .is_ptr: $node.is_ptr */ ')
g.write(node.name)
}
ast.EnumVal {
// g.write('${it.mod}${it.enum_name}_$it.val')
styp := g.typ(node.typ)
@ -4366,6 +4403,11 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) ?bool {
g.write('($s)')
}
}
if expr is ast.CTempVar {
if expr.is_ptr {
g.write('*')
}
}
g.expr(expr)
if sym.kind == .struct_ && !sym_has_str_method {
if is_p {

View File

@ -520,6 +520,9 @@ fn (mut g JsGen) stmt(node ast.Stmt) {
fn (mut g JsGen) expr(node ast.Expr) {
match node {
ast.CTempVar {
g.write('/* ast.CTempVar: node.name */')
}
ast.AnonFn {
g.gen_fn_decl(node.decl)
}
@ -673,17 +676,17 @@ fn (mut g JsGen) gen_assert_stmt(a ast.AssertStmt) {
mut mod_path := g.file.path.replace('\\', '\\\\')
if g.is_test {
g.writeln(' g_test_oks++;')
g.writeln(' cb_assertion_ok("$mod_path", ${a.pos.line_nr+1}, "assert $s_assertion", "${g.fn_decl.name}()" );')
g.writeln(' cb_assertion_ok("$mod_path", ${a.pos.line_nr + 1}, "assert $s_assertion", "${g.fn_decl.name}()" );')
g.writeln('} else {')
g.writeln(' g_test_fails++;')
g.writeln(' cb_assertion_failed("$mod_path", ${a.pos.line_nr+1}, "assert $s_assertion", "${g.fn_decl.name}()" );')
g.writeln(' cb_assertion_failed("$mod_path", ${a.pos.line_nr + 1}, "assert $s_assertion", "${g.fn_decl.name}()" );')
g.writeln(' exit(1);')
g.writeln('}')
return
}
g.writeln('} else {')
g.inc_indent()
g.writeln('builtin.eprintln("$mod_path:${a.pos.line_nr+1}: FAIL: fn ${g.fn_decl.name}(): assert $s_assertion");')
g.writeln('builtin.eprintln("$mod_path:${a.pos.line_nr + 1}: FAIL: fn ${g.fn_decl.name}(): assert $s_assertion");')
g.writeln('builtin.exit(1);')
g.dec_indent()
g.writeln('}')