From a9435f3c25cc42d35359097473ffae102216a7c5 Mon Sep 17 00:00:00 2001
From: zakuro <z@kuro.red>
Date: Thu, 13 May 2021 23:54:48 +0900
Subject: [PATCH] cgen: fix `cannot take rvalue` error of interface (#10040)

---
 vlib/v/gen/c/cgen.v               | 76 +++++++++++++++++--------------
 vlib/v/tests/interface_test.v     |  6 +++
 vlib/v/tests/sumtype_calls_test.v |  4 ++
 3 files changed, 53 insertions(+), 33 deletions(-)

diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v
index 53368205d0..da6d1a275d 100644
--- a/vlib/v/gen/c/cgen.v
+++ b/vlib/v/gen/c/cgen.v
@@ -1768,6 +1768,26 @@ fn (mut g Gen) write_sumtype_casting_fn(got_ ast.Type, exp_ ast.Type) {
 	g.auto_fn_definitions << sb.str()
 }
 
+fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp_is_ptr bool, exp_styp string, got_is_ptr bool, got_styp string) {
+	mut rparen_n := 1
+	if exp_is_ptr {
+		g.write('HEAP($exp_styp, ')
+		rparen_n++
+	}
+	g.write('${fname}(')
+	if !got_is_ptr {
+		if !expr.is_lvalue()
+			|| (expr is ast.Ident && is_simple_define_const((expr as ast.Ident).obj)) {
+			g.write('ADDR($got_styp, (')
+			rparen_n += 2
+		} else {
+			g.write('&')
+		}
+	}
+	g.expr(expr)
+	g.write(')'.repeat(rparen_n))
+}
+
 // use instead of expr() when you need to cast to a different type
 fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_type ast.Type) {
 	got_type := g.table.mktyp(got_type_raw)
@@ -1787,18 +1807,9 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
 		&& !expected_type.has_flag(.optional) {
 		got_styp := g.cc_type(got_type, true)
 		exp_styp := g.cc_type(expected_type, true)
-		if expected_is_ptr {
-			g.write('HEAP($exp_styp, ')
-		}
-		g.write('I_${got_styp}_to_Interface_${exp_styp}(')
-		if !got_is_ptr {
-			g.write('&')
-		}
-		g.expr(expr)
-		g.write(')')
-		if expected_is_ptr {
-			g.write(')')
-		}
+		fname := 'I_${got_styp}_to_Interface_$exp_styp'
+		g.call_cfn_for_casting_expr(fname, expr, expected_is_ptr, exp_styp, got_is_ptr,
+			got_styp)
 		return
 	}
 	// cast to sum type
@@ -1827,21 +1838,9 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
 				g.expr(expr)
 			} else {
 				g.write_sumtype_casting_fn(got_type, expected_type)
-				if expected_is_ptr {
-					g.write('HEAP($exp_sym.cname, ')
-				}
-				g.write('${got_sym.cname}_to_sumtype_${exp_sym.cname}(')
-				if !got_is_ptr {
-					g.write('ADDR($got_styp, (')
-					g.expr(expr)
-					g.write(')))')
-				} else {
-					g.expr(expr)
-					g.write(')')
-				}
-				if expected_is_ptr {
-					g.write(')')
-				}
+				fname := '${got_sym.cname}_to_sumtype_$exp_sym.cname'
+				g.call_cfn_for_casting_expr(fname, expr, expected_is_ptr, exp_sym.cname,
+					got_is_ptr, got_styp)
 			}
 			return
 		}
@@ -5113,11 +5112,6 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
 		*/
 		field_expr := field.expr
 		match field.expr {
-			ast.CharLiteral, ast.FloatLiteral, ast.IntegerLiteral {
-				// "Simple" expressions are not going to need multiple statements,
-				// only the ones which are inited later, so it's safe to use expr_string
-				g.const_decl_simple_define(name, g.expr_string(field_expr))
-			}
 			ast.ArrayInit {
 				if field.expr.is_fixed {
 					styp := g.typ(field.expr.typ)
@@ -5147,12 +5141,28 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
 				}
 			}
 			else {
-				g.const_decl_init_later(field.mod, name, field.expr, field.typ, false)
+				if is_simple_define_const(field) {
+					// "Simple" expressions are not going to need multiple statements,
+					// only the ones which are inited later, so it's safe to use expr_string
+					g.const_decl_simple_define(name, g.expr_string(field_expr))
+				} else {
+					g.const_decl_init_later(field.mod, name, field.expr, field.typ, false)
+				}
 			}
 		}
 	}
 }
 
+fn is_simple_define_const(obj ast.ScopeObject) bool {
+	if obj is ast.ConstField {
+		return match obj.expr {
+			ast.CharLiteral, ast.FloatLiteral, ast.IntegerLiteral { true }
+			else { false }
+		}
+	}
+	return false
+}
+
 fn (mut g Gen) const_decl_simple_define(name string, val string) {
 	// Simple expressions should use a #define
 	// so that we don't pollute the binary with unnecessary global vars
diff --git a/vlib/v/tests/interface_test.v b/vlib/v/tests/interface_test.v
index 317f510a9b..388fd555ad 100644
--- a/vlib/v/tests/interface_test.v
+++ b/vlib/v/tests/interface_test.v
@@ -7,6 +7,10 @@ mut:
 	breed string
 }
 
+fn new_cat(breed string) Cat {
+	return Cat{breed}
+}
+
 fn (c &Cat) name() string {
 	if c.breed != '' {
 		assert c.breed == 'Persian'
@@ -101,10 +105,12 @@ fn test_perform_speak() {
 	perform_speak(Cat{
 		breed: 'Persian'
 	})
+	perform_speak(new_cat('Persian'))
 	perform_speak_on_ptr(cat)
 	perform_speak_on_ptr(Cat{
 		breed: 'Persian'
 	})
+	perform_speak_on_ptr(new_cat('Persian'))
 	handle_animals([dog, cat])
 	/*
 	f := Foo {
diff --git a/vlib/v/tests/sumtype_calls_test.v b/vlib/v/tests/sumtype_calls_test.v
index 8a68736511..d2680f2647 100644
--- a/vlib/v/tests/sumtype_calls_test.v
+++ b/vlib/v/tests/sumtype_calls_test.v
@@ -56,9 +56,13 @@ fn test_sum_type_cast() {
 fn test_sum_types() {
 	b := parse_bool()
 	handle_expr(b)
+	handle_expr(parse_bool())
+
 	de := DeclExprA{}
 	handle_expr(de)
 	handle_decl_expr(de)
+	handle_expr(DeclExprA{})
+	handle_decl_expr(DeclExprA{})
 }
 
 /*