cmd/cgo: support large unsigned macros

Currently, cgo converts integer macros into int64 if it's possible.
As a result, some macros which satisfy

math.MaxInt64 < x <= math.MaxUint64

will lose their original values.

This CL introduces the new probe to check signs,
so we can handle signed ints and unsigned ints separately.

Fixes #20369

Change-Id: I002ba452a82514b3a87440960473676f842cc9ee
Reviewed-on: https://go-review.googlesource.com/43476
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Hiroshi Ioka 2017-05-16 21:52:41 +09:00 committed by Ian Lance Taylor
parent c20e54533e
commit 7555a45dc2
4 changed files with 54 additions and 9 deletions

View file

@ -76,5 +76,6 @@ func TestThreadLock(t *testing.T) { testThreadLockFunc(t) }
func TestCheckConst(t *testing.T) { testCheckConst(t) }
func Test17537(t *testing.T) { test17537(t) }
func Test18126(t *testing.T) { test18126(t) }
func Test20369(t *testing.T) { test20369(t) }
func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }

View file

@ -0,0 +1,20 @@
// Copyright 2017 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
/*
#define UINT64_MAX 18446744073709551615ULL
*/
import "C"
import (
"math"
"testing"
)
func test20369(t *testing.T) {
if C.UINT64_MAX != math.MaxUint64 {
t.Fatalf("got %v, want %v", uint64(C.UINT64_MAX), uint64(math.MaxUint64))
}
}

View file

@ -305,12 +305,18 @@ func (p *Package) guessKinds(f *File) []*Name {
// void __cgo_f_xxx_4(void) { static const double x = (name); }
// #line xxx "not-str-lit"
// void __cgo_f_xxx_5(void) { static const char x[] = (name); }
// #line xxx "not-signed-int-const"
// #if 0 < -(name)
// #line xxx "not-signed-int-const"
// #error found unsigned int
// #endif
//
// If we see an error at not-declared:xxx, the corresponding name is not declared.
// If we see an error at not-type:xxx, the corresponding name is a type.
// If we see an error at not-int-const:xxx, the corresponding name is not an integer constant.
// If we see an error at not-num-const:xxx, the corresponding name is not a number constant.
// If we see an error at not-str-lit:xxx, the corresponding name is not a string literal.
// If we see an error at not-signed-int-const:xxx, the corresponding name is not a signed integer literal.
//
// The specific input forms are chosen so that they are valid C syntax regardless of
// whether name denotes a type or an expression.
@ -329,12 +335,19 @@ func (p *Package) guessKinds(f *File) []*Name {
"#line %d \"not-num-const\"\n"+
"void __cgo_f_%d_4(void) { static const double x = (%s); }\n"+
"#line %d \"not-str-lit\"\n"+
"void __cgo_f_%d_5(void) { static const char s[] = (%s); }\n",
"void __cgo_f_%d_5(void) { static const char s[] = (%s); }\n"+
"#line %d \"not-signed-int-const\"\n"+
"#if 0 < (%s)\n"+
"#line %d \"not-signed-int-const\"\n"+
"#error found unsigned int\n"+
"#endif\n",
i+1, i+1, n.C,
i+1, i+1, n.C,
i+1, i+1, n.C,
i+1, i+1, n.C,
i+1, i+1, n.C)
i+1, i+1, n.C,
i+1, n.C, i+1,
)
}
fmt.Fprintf(&b, "#line 1 \"completed\"\n"+
"int __cgo__1 = __cgo__2;\n")
@ -352,6 +365,7 @@ func (p *Package) guessKinds(f *File) []*Name {
notNumConst
notStrLiteral
notDeclared
notSignedIntConst
)
for _, line := range strings.Split(stderr, "\n") {
if !strings.Contains(line, ": error:") {
@ -395,6 +409,8 @@ func (p *Package) guessKinds(f *File) []*Name {
sniff[i] |= notNumConst
case "not-str-lit":
sniff[i] |= notStrLiteral
case "not-signed-int-const":
sniff[i] |= notSignedIntConst
}
}
@ -403,11 +419,15 @@ func (p *Package) guessKinds(f *File) []*Name {
}
for i, n := range names {
switch sniff[i] {
switch sniff[i] &^ notSignedIntConst {
default:
error_(token.NoPos, "could not determine kind of name for C.%s", fixGo(n.Go))
case notStrLiteral | notType:
n.Kind = "iconst"
if sniff[i]&notSignedIntConst != 0 {
n.Kind = "uconst"
} else {
n.Kind = "iconst"
}
case notIntConst | notStrLiteral | notType:
n.Kind = "fconst"
case notIntConst | notNumConst | notType:
@ -452,7 +472,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
b.WriteString("#line 1 \"cgo-dwarf-inference\"\n")
for i, n := range names {
fmt.Fprintf(&b, "__typeof__(%s) *__cgo__%d;\n", n.C, i)
if n.Kind == "iconst" {
if n.Kind == "iconst" || n.Kind == "uconst" {
fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C)
}
}
@ -461,7 +481,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
// so we can read them out of the object file.
fmt.Fprintf(&b, "long long __cgodebug_ints[] = {\n")
for _, n := range names {
if n.Kind == "iconst" {
if n.Kind == "iconst" || n.Kind == "uconst" {
fmt.Fprintf(&b, "\t%s,\n", n.C)
} else {
fmt.Fprintf(&b, "\t0,\n")
@ -564,6 +584,10 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
if i < len(ints) {
n.Const = fmt.Sprintf("%#x", ints[i])
}
case "uconst":
if i < len(ints) {
n.Const = fmt.Sprintf("%#x", uint64(ints[i]))
}
case "fconst":
if i < len(floats) {
n.Const = fmt.Sprintf("%f", floats[i])

View file

@ -88,7 +88,7 @@ type Name struct {
Mangle string // name used in generated Go
C string // name used in C
Define string // #define expansion
Kind string // "iconst", "fconst", "sconst", "type", "var", "fpvar", "func", "not-type"
Kind string // "iconst", "uconst", "fconst", "sconst", "type", "var", "fpvar", "func", "not-type"
Type *Type // the type of xxx
FuncType *FuncType
AddError bool
@ -100,9 +100,9 @@ func (n *Name) IsVar() bool {
return n.Kind == "var" || n.Kind == "fpvar"
}
// IsConst reports whether Kind is either "iconst", "fconst" or "sconst"
// IsConst reports whether Kind is either "iconst", "uconst", "fconst" or "sconst"
func (n *Name) IsConst() bool {
return n.Kind == "iconst" || n.Kind == "fconst" || n.Kind == "sconst"
return strings.HasSuffix(n.Kind, "const")
}
// A ExpFunc is an exported function, callable from C.