1
0
mirror of https://github.com/golang/go synced 2024-11-26 03:27:58 -07:00

cmd/cgo: use C exact-width integer types to represent Go types

The exact-width integer types are required to use two’s complement
representation and may not have padding bits, cf. §7.20.1.1/1 in the C11
standard or https://en.cppreference.com/w/c/types/integer.  This ensures that
they have the same domain and representation as the corresponding Go types.

Fixes #29878
This commit is contained in:
Philipp Stephani 2019-01-23 23:48:46 +01:00
parent 0a04c0430e
commit 546a2cc3f1
6 changed files with 89 additions and 22 deletions

View File

@ -56,6 +56,7 @@ func Test25143(t *testing.T) { test25143(t) }
func Test26066(t *testing.T) { test26066(t) } func Test26066(t *testing.T) { test26066(t) }
func Test27660(t *testing.T) { test27660(t) } func Test27660(t *testing.T) { test27660(t) }
func Test28896(t *testing.T) { test28896(t) } func Test28896(t *testing.T) { test28896(t) }
func Test29878(t *testing.T) { test29878(t) }
func Test30065(t *testing.T) { test30065(t) } func Test30065(t *testing.T) { test30065(t) }
func TestAlign(t *testing.T) { testAlign(t) } func TestAlign(t *testing.T) { testAlign(t) }
func TestAtol(t *testing.T) { testAtol(t) } func TestAtol(t *testing.T) { testAtol(t) }

View File

@ -0,0 +1,20 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cgotest
// #include <stdint.h>
// uint64_t issue29878exported(int8_t); // prototype must match
// int16_t issue29878function(uint32_t arg) { return issue29878exported(arg); }
import "C"
import "testing"
func test29878(t *testing.T) {
const arg uint32 = 123 // fits into all integer types
var ret int16 = C.issue29878function(arg) // no conversions needed
if int64(ret) != int64(arg) {
t.Errorf("return value unexpected: got %d, want %d", ret, arg)
}
}

View File

@ -0,0 +1,12 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cgotest
import "C"
//export issue29878exported
func issue29878exported(arg int8) uint64 {
return uint64(arg)
}

View File

@ -148,6 +148,8 @@ C.long, C.ulong (unsigned long), C.longlong (long long),
C.ulonglong (unsigned long long), C.float, C.double, C.ulonglong (unsigned long long), C.float, C.double,
C.complexfloat (complex float), and C.complexdouble (complex double). C.complexfloat (complex float), and C.complexdouble (complex double).
The C type void* is represented by Go's unsafe.Pointer. The C type void* is represented by Go's unsafe.Pointer.
The C sized integer types (int8_t, uint8_t, ) are represented by their Go
counterparts (int8, uint8, ).
The C types __int128_t and __uint128_t are represented by [16]byte. The C types __int128_t and __uint128_t are represented by [16]byte.
A few special C types which would normally be represented by a pointer A few special C types which would normally be represented by a pointer
@ -296,7 +298,7 @@ Go functions can be exported for use by C code in the following way:
They will be available in the C code as: They will be available in the C code as:
extern int64 MyFunction(int arg1, int arg2, GoString arg3); extern int64_t MyFunction(int arg1, int arg2, GoString arg3);
extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3); extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3);
found in the _cgo_export.h generated header, after any preambles found in the _cgo_export.h generated header, after any preambles

View File

@ -23,6 +23,7 @@ import (
"internal/xcoff" "internal/xcoff"
"math" "math"
"os" "os"
"regexp"
"strconv" "strconv"
"strings" "strings"
"unicode" "unicode"
@ -2046,6 +2047,8 @@ type typeConv struct {
ptrSize int64 ptrSize int64
intSize int64 intSize int64
exactWidthIntegerTypes map[string]*Type
} }
var tagGen int var tagGen int
@ -2088,6 +2091,21 @@ func (c *typeConv) Init(ptrSize, intSize int64) {
} else { } else {
c.goVoidPtr = c.Ident("unsafe.Pointer") c.goVoidPtr = c.Ident("unsafe.Pointer")
} }
c.exactWidthIntegerTypes = make(map[string]*Type)
for _, t := range []ast.Expr{
c.int8, c.int16, c.int32, c.int64,
c.uint8, c.uint16, c.uint32, c.uint64,
} {
name := t.(*ast.Ident).Name
u := new(Type)
*u = *goTypes[name]
if u.Align > ptrSize {
u.Align = ptrSize
}
u.Go = t
c.exactWidthIntegerTypes[name] = u
}
} }
// base strips away qualifiers and typedefs to get the underlying type // base strips away qualifiers and typedefs to get the underlying type
@ -2459,6 +2477,24 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
t.Align = c.ptrSize t.Align = c.ptrSize
break break
} }
// Exact-width integer types. These are always compatible with
// the corresponding Go types since the C standard requires
// them to have no padding bit and use the twos complement
// representation.
if exactWidthIntegerType.MatchString(dt.Name) {
sub := c.Type(dt.Type, pos)
u := c.exactWidthIntegerTypes[strings.TrimSuffix(dt.Name, "_t")]
if sub.Size != u.Size {
fatalf("%s: unexpected size: %d vs. %d %s", lineno(pos), sub.Size, u.Size, dtype)
}
if sub.Align != u.Align {
fatalf("%s: unexpected alignment: %d vs. %d %s", lineno(pos), sub.Align, u.Align, dtype)
}
t.Size = u.Size
t.Align = u.Align
t.Go = u.Go
break
}
name := c.Ident("_Ctype_" + dt.Name) name := c.Ident("_Ctype_" + dt.Name)
goIdent[name.Name] = name goIdent[name.Name] = name
sub := c.Type(dt.Type, pos) sub := c.Type(dt.Type, pos)
@ -2594,6 +2630,8 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
return t return t
} }
var exactWidthIntegerType = regexp.MustCompile(`^u?int(8|16|32|64)_t$`)
// isStructUnionClass reports whether the type described by the Go syntax x // isStructUnionClass reports whether the type described by the Go syntax x
// is a struct, union, or class with a tag. // is a struct, union, or class with a tag.
func isStructUnionClass(x ast.Expr) bool { func isStructUnionClass(x ast.Expr) bool {

View File

@ -1361,19 +1361,19 @@ func c(repr string, args ...interface{}) *TypeRepr {
// Map predeclared Go types to Type. // Map predeclared Go types to Type.
var goTypes = map[string]*Type{ var goTypes = map[string]*Type{
"bool": {Size: 1, Align: 1, C: c("GoUint8")}, "bool": {Size: 1, Align: 1, C: c("uint8_t")},
"byte": {Size: 1, Align: 1, C: c("GoUint8")}, "byte": {Size: 1, Align: 1, C: c("uint8_t")},
"int": {Size: 0, Align: 0, C: c("GoInt")}, "int": {Size: 0, Align: 0, C: c("GoInt")},
"uint": {Size: 0, Align: 0, C: c("GoUint")}, "uint": {Size: 0, Align: 0, C: c("GoUint")},
"rune": {Size: 4, Align: 4, C: c("GoInt32")}, "rune": {Size: 4, Align: 4, C: c("int32_t")},
"int8": {Size: 1, Align: 1, C: c("GoInt8")}, "int8": {Size: 1, Align: 1, C: c("int8_t")},
"uint8": {Size: 1, Align: 1, C: c("GoUint8")}, "uint8": {Size: 1, Align: 1, C: c("uint8_t")},
"int16": {Size: 2, Align: 2, C: c("GoInt16")}, "int16": {Size: 2, Align: 2, C: c("int16_t")},
"uint16": {Size: 2, Align: 2, C: c("GoUint16")}, "uint16": {Size: 2, Align: 2, C: c("uint16_t")},
"int32": {Size: 4, Align: 4, C: c("GoInt32")}, "int32": {Size: 4, Align: 4, C: c("int32_t")},
"uint32": {Size: 4, Align: 4, C: c("GoUint32")}, "uint32": {Size: 4, Align: 4, C: c("uint32_t")},
"int64": {Size: 8, Align: 8, C: c("GoInt64")}, "int64": {Size: 8, Align: 8, C: c("int64_t")},
"uint64": {Size: 8, Align: 8, C: c("GoUint64")}, "uint64": {Size: 8, Align: 8, C: c("uint64_t")},
"float32": {Size: 4, Align: 4, C: c("GoFloat32")}, "float32": {Size: 4, Align: 4, C: c("GoFloat32")},
"float64": {Size: 8, Align: 8, C: c("GoFloat64")}, "float64": {Size: 8, Align: 8, C: c("GoFloat64")},
"complex64": {Size: 8, Align: 4, C: c("GoComplex64")}, "complex64": {Size: 8, Align: 4, C: c("GoComplex64")},
@ -1865,16 +1865,10 @@ const gccExportHeaderProlog = `
#ifndef GO_CGO_PROLOGUE_H #ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H #define GO_CGO_PROLOGUE_H
typedef signed char GoInt8; #include <stdint.h>
typedef unsigned char GoUint8;
typedef short GoInt16; typedef intGOINTBITS_t GoInt;
typedef unsigned short GoUint16; typedef uintGOINTBITS_t GoUint;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoIntGOINTBITS GoInt;
typedef GoUintGOINTBITS GoUint;
typedef __SIZE_TYPE__ GoUintptr; typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32; typedef float GoFloat32;
typedef double GoFloat64; typedef double GoFloat64;