From a3ca6562407770c45d9f358504e535c5bffcd0f4 Mon Sep 17 00:00:00 2001 From: kamphaus Date: Mon, 4 Jun 2018 01:54:49 +0200 Subject: [PATCH] Pass fixed-size arrays as slices. Fixes #736 (#737) When passing arrays of fixed length around as parameters, Go passes them by value, making a copy. Any changes to the copied array will not change the initial array values. To have the same behaviour as in C, we need to always pass arrays as slices. This is also required for calls to library functions like UnsafeSliceToSlice and Fread. --- noarch/stdio.go | 4 ++-- program/function_definition.go | 2 +- tests/string.c | 29 ++++++++++++++++++++++++++++- transpiler/call.go | 10 +++++++--- transpiler/cast.go | 2 +- transpiler/variables.go | 5 +++++ types/cast.go | 3 +++ 7 files changed, 47 insertions(+), 8 deletions(-) diff --git a/noarch/stdio.go b/noarch/stdio.go index 135727b3b..b5add322d 100644 --- a/noarch/stdio.go +++ b/noarch/stdio.go @@ -626,7 +626,7 @@ func Ftell(f *File) int32 { // read. // // The total amount of bytes read if successful is (size*count). -func Fread(ptr *[]byte, size1, size2 int32, f *File) int32 { +func Fread(ptr []byte, size1, size2 int32, f *File) int32 { // Create a new buffer so that we can ensure we read up to the correct // number of bytes from the file. newBuffer := make([]byte, size1*size2) @@ -635,7 +635,7 @@ func Fread(ptr *[]byte, size1, size2 int32, f *File) int32 { // Despite any error we need to make sure the bytes read are copied to the // destination buffer. for i, b := range newBuffer { - (*ptr)[i] = b + ptr[i] = b } // Now we can handle the success or failure. diff --git a/program/function_definition.go b/program/function_definition.go index d32a2cd1b..ef7045ca7 100644 --- a/program/function_definition.go +++ b/program/function_definition.go @@ -164,7 +164,7 @@ var builtInFunctionDefinitions = map[string][]string{ "int putc(int, FILE*) -> noarch.Fputc", "int fseek(FILE*, long int, int) -> noarch.Fseek", "long ftell(FILE*) -> noarch.Ftell", - "int fread(void*, int, int, FILE*) -> $0 = noarch.Fread(&1, $2, $3, $4)", + "int fread(void*, int, int, FILE*) -> noarch.Fread", "int fwrite(char*, int, int, FILE*) -> noarch.Fwrite", "int fgetpos(FILE*, int*) -> noarch.Fgetpos", "int fsetpos(FILE*, int*) -> noarch.Fsetpos", diff --git a/tests/string.c b/tests/string.c index 1e830a4e7..06382dc23 100644 --- a/tests/string.c +++ b/tests/string.c @@ -6,9 +6,21 @@ typedef struct mem { int b; } mem; +typedef struct mem2 { + int a[2]; +} mem2; +typedef int altint; + +void setptr(int *arr, int val) { + arr[0] = val; +} +void setarr(int arr[], int val) { + arr[0] = val; +} + int main() { - plan(74); + plan(80); diag("TODO: __builtin_object_size") // https://github.com/elliotchance/c2go/issues/359 @@ -244,6 +256,21 @@ int main() is_eq(dest7[0].b, 3.0); is_eq(dest7[1].a, 0); is_eq(dest7[1].b, 0.0); + mem2 dest8; + dest8.a[0] = 42; + memset(dest8.a, 0, sizeof(int)*2); + is_eq(dest8.a[0], 0); + is_eq(dest8.a[1], 0); + dest8.a[0] = 42; + mem2 dest9; + altint *test = (altint *) dest9.a; + memcpy(dest9.a, dest8.a, sizeof(int)*2); + is_eq(dest9.a[0], 42); + is_eq(test[0], 42); + setarr(dest9.a, 1); + is_eq(dest9.a[0], 1); + setptr(dest9.a, 2); + is_eq(dest9.a[0], 2); } { diag("memcmp"); diff --git a/transpiler/call.go b/transpiler/call.go index 7e4f7b255..09f1bb761 100644 --- a/transpiler/call.go +++ b/transpiler/call.go @@ -207,9 +207,13 @@ func transpileCallExpr(n *ast.CallExpr, p *program.Program) ( var varName string if v, ok := element[0].(*goast.Ident); ok { varName = v.Name - } else { - return nil, "", nil, nil, - fmt.Errorf("golang ast for variable name have type %T, expect ast.Ident", element[3]) + } else if se, ok := element[0].(*goast.SliceExpr); ok { + if v, ok2 := se.X.(*goast.Ident); ok2 { + varName = v.Name + } else { + return nil, "", nil, nil, + fmt.Errorf("golang ast for variable name have type %T, expect ast.Ident", element[0]) + } } p.AddImport("sort") diff --git a/transpiler/cast.go b/transpiler/cast.go index ec3b0e7c8..c9680b921 100644 --- a/transpiler/cast.go +++ b/transpiler/cast.go @@ -59,7 +59,7 @@ func transpileImplicitCastExpr(n *ast.ImplicitCastExpr, p *program.Program, expr return } - if !types.IsFunction(exprType) && n.Kind != ast.ImplicitCastExprArrayToPointerDecay { + if !types.IsFunction(exprType) && !strings.ContainsAny(n.Type, "[]") { expr, err = types.CastExpr(p, expr, exprType, n.Type) if err != nil { return nil, "", nil, nil, err diff --git a/transpiler/variables.go b/transpiler/variables.go index 2ba1d0118..acad7853b 100644 --- a/transpiler/variables.go +++ b/transpiler/variables.go @@ -370,6 +370,11 @@ func transpileArraySubscriptExpr(n *ast.ArraySubscriptExpr, p *program.Program, } preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost) + if se, ok := expression.(*goast.SliceExpr); ok && se.High == nil && se.Low == nil && se.Max == nil { + // simplify the expression + expression = se.X + } + isConst, indexInt := util.EvaluateConstExpr(index) if isConst && indexInt < 0 { indexInt = -indexInt diff --git a/types/cast.go b/types/cast.go index 9b0688ef6..9205cefc2 100644 --- a/types/cast.go +++ b/types/cast.go @@ -490,6 +490,9 @@ func CastExpr(p *program.Program, expr goast.Expr, cFromType, cToType string) ( if err != nil { return nil, err } + if _, arrSize := GetArrayTypeAndSize(cFromType); arrSize > 0 { + expr = &goast.SliceExpr{X: expr} + } return &goast.StarExpr{ X: &goast.CallExpr{ Fun: &goast.StarExpr{