diff --git a/compiler/expressions.go b/compiler/expressions.go index 5d9e37cc9..578c3f09d 100644 --- a/compiler/expressions.go +++ b/compiler/expressions.go @@ -206,6 +206,8 @@ func (fc *funcContext) translateExpr(expr ast.Expr) *expression { return fc.translateExpr(e.X) } + elemType := exprType.(*types.Pointer).Elem() + switch x := astutil.RemoveParens(e.X).(type) { case *ast.CompositeLit: return fc.formatExpr("$newDataPointer(%e, %s)", x, fc.typeName(fc.pkgCtx.TypeOf(e))) @@ -214,13 +216,13 @@ func (fc *funcContext) translateExpr(expr ast.Expr) *expression { if fc.pkgCtx.escapingVars[obj] { return fc.formatExpr("(%1s.$ptr || (%1s.$ptr = new %2s(function() { return this.$target[0]; }, function($v) { this.$target[0] = $v; }, %1s)))", fc.pkgCtx.objectNames[obj], fc.typeName(exprType)) } - return fc.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, fc.varPtrName(obj), fc.typeName(exprType), fc.objectName(obj), fc.translateAssign(x, fc.newIdent("$v", exprType), false)) + return fc.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, fc.varPtrName(obj), fc.typeName(exprType), fc.objectName(obj), fc.translateAssign(x, fc.newIdent("$v", elemType), false)) case *ast.SelectorExpr: sel, ok := fc.pkgCtx.SelectionOf(x) if !ok { // qualified identifier obj := fc.pkgCtx.Uses[x.Sel].(*types.Var) - return fc.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, fc.varPtrName(obj), fc.typeName(exprType), fc.objectName(obj), fc.translateAssign(x, fc.newIdent("$v", exprType), false)) + return fc.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, fc.varPtrName(obj), fc.typeName(exprType), fc.objectName(obj), fc.translateAssign(x, fc.newIdent("$v", elemType), false)) } newSel := &ast.SelectorExpr{X: fc.newIdent("this.$target", fc.pkgCtx.TypeOf(x.X)), Sel: x.Sel} fc.setType(newSel, exprType) @@ -1183,7 +1185,7 @@ func (fc *funcContext) translateConversion(expr ast.Expr, desiredType types.Type // // TODO(nevkontakte): Should this only apply when exprType is a pointer to a // struct as well? - return fc.formatExpr("$pointerOfStructConversion(%e, %s)", expr, fc.typeName(t)) + return fc.formatExpr("$pointerOfStructConversion(%e, %s)", expr, fc.typeName(desiredType)) } if types.Identical(exprType, types.Typ[types.UnsafePointer]) { @@ -1213,12 +1215,7 @@ func (fc *funcContext) translateConversion(expr ast.Expr, desiredType types.Type func (fc *funcContext) translateImplicitConversionWithCloning(expr ast.Expr, desiredType types.Type) *expression { switch desiredType.Underlying().(type) { case *types.Struct, *types.Array: - switch expr.(type) { - case nil, *ast.CompositeLit: - // nothing - default: - return fc.formatExpr("$clone(%e, %s)", expr, fc.typeName(desiredType)) - } + return fc.formatExpr("$clone(%e, %s)", expr, fc.typeName(desiredType)) } return fc.translateImplicitConversion(expr, desiredType) diff --git a/compiler/statements.go b/compiler/statements.go index fc36453a4..f8d791948 100644 --- a/compiler/statements.go +++ b/compiler/statements.go @@ -719,7 +719,7 @@ func (fc *funcContext) translateAssign(lhs, rhs ast.Expr, define bool) string { } lhsType := fc.pkgCtx.TypeOf(lhs) - rhsExpr := fc.translateImplicitConversion(rhs, lhsType) + rhsExpr := fc.translateConversion(rhs, lhsType) if _, ok := rhs.(*ast.CompositeLit); ok && define { return fmt.Sprintf("%s = %s;", fc.translateExpr(lhs), rhsExpr) // skip $copy } diff --git a/tests/misc_test.go b/tests/misc_test.go index 01d3f7d03..3608d126b 100644 --- a/tests/misc_test.go +++ b/tests/misc_test.go @@ -187,6 +187,8 @@ func TestPointerOfStructConversion(t *testing.T) { type B A + type AP *A + a1 := &A{Value: 1} b1 := (*B)(a1) b1.Value = 2 @@ -197,6 +199,10 @@ func TestPointerOfStructConversion(t *testing.T) { if a1 != a2 || b1 != b2 || a1.Value != 4 || a2.Value != 4 || b1.Value != 4 || b2.Value != 4 { t.Fail() } + + if got := reflect.TypeOf((AP)(&A{Value: 1})); got.String() != "tests.AP" { + t.Errorf("Got: reflect.TypeOf((AP)(&A{Value: 1})) = %v. Want: tests.AP.", got) + } } func TestCompareStruct(t *testing.T) { @@ -888,3 +894,29 @@ func TestReflectSetForEmbed(t *testing.T) { t.Fatalf("relfect.Set got %v, want %v", f0, e.Field(0)) } } + +func TestAssignImplicitConversion(t *testing.T) { + type S struct{} + type SP *S + + t.Run("Pointer to named type", func(t *testing.T) { + var sp SP = &S{} + if got := reflect.TypeOf(sp); got.String() != "tests.SP" { + t.Errorf("Got: reflect.TypeOf(sp) = %v. Want: tests.SP", got) + } + }) + + t.Run("Anonymous struct to named type", func(t *testing.T) { + var s S = struct{}{} + if got := reflect.TypeOf(s); got.String() != "tests.S" { + t.Errorf("Got: reflect.TypeOf(s) = %v. Want: tests.S", got) + } + }) + + t.Run("Named type to anonymous type", func(t *testing.T) { + var x struct{} = S{} + if got := reflect.TypeOf(x); got.String() != "struct {}" { + t.Errorf("Got: reflect.TypeOf(x) = %v. Want: struct {}", got) + } + }) +}