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 TestCheckConst(t *testing.T) { testCheckConst(t) }
func Test17537(t *testing.T) { test17537(t) } func Test17537(t *testing.T) { test17537(t) }
func Test18126(t *testing.T) { test18126(t) } func Test18126(t *testing.T) { test18126(t) }
func Test20369(t *testing.T) { test20369(t) }
func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } 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); } // void __cgo_f_xxx_4(void) { static const double x = (name); }
// #line xxx "not-str-lit" // #line xxx "not-str-lit"
// void __cgo_f_xxx_5(void) { static const char x[] = (name); } // 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-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-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-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-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-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 // The specific input forms are chosen so that they are valid C syntax regardless of
// whether name denotes a type or an expression. // 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"+ "#line %d \"not-num-const\"\n"+
"void __cgo_f_%d_4(void) { static const double x = (%s); }\n"+ "void __cgo_f_%d_4(void) { static const double x = (%s); }\n"+
"#line %d \"not-str-lit\"\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, 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"+ fmt.Fprintf(&b, "#line 1 \"completed\"\n"+
"int __cgo__1 = __cgo__2;\n") "int __cgo__1 = __cgo__2;\n")
@ -352,6 +365,7 @@ func (p *Package) guessKinds(f *File) []*Name {
notNumConst notNumConst
notStrLiteral notStrLiteral
notDeclared notDeclared
notSignedIntConst
) )
for _, line := range strings.Split(stderr, "\n") { for _, line := range strings.Split(stderr, "\n") {
if !strings.Contains(line, ": error:") { if !strings.Contains(line, ": error:") {
@ -395,6 +409,8 @@ func (p *Package) guessKinds(f *File) []*Name {
sniff[i] |= notNumConst sniff[i] |= notNumConst
case "not-str-lit": case "not-str-lit":
sniff[i] |= notStrLiteral 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 { for i, n := range names {
switch sniff[i] { switch sniff[i] &^ notSignedIntConst {
default: default:
error_(token.NoPos, "could not determine kind of name for C.%s", fixGo(n.Go)) error_(token.NoPos, "could not determine kind of name for C.%s", fixGo(n.Go))
case notStrLiteral | notType: case notStrLiteral | notType:
n.Kind = "iconst" if sniff[i]&notSignedIntConst != 0 {
n.Kind = "uconst"
} else {
n.Kind = "iconst"
}
case notIntConst | notStrLiteral | notType: case notIntConst | notStrLiteral | notType:
n.Kind = "fconst" n.Kind = "fconst"
case notIntConst | notNumConst | notType: case notIntConst | notNumConst | notType:
@ -452,7 +472,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
b.WriteString("#line 1 \"cgo-dwarf-inference\"\n") b.WriteString("#line 1 \"cgo-dwarf-inference\"\n")
for i, n := range names { for i, n := range names {
fmt.Fprintf(&b, "__typeof__(%s) *__cgo__%d;\n", n.C, i) 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) 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. // so we can read them out of the object file.
fmt.Fprintf(&b, "long long __cgodebug_ints[] = {\n") fmt.Fprintf(&b, "long long __cgodebug_ints[] = {\n")
for _, n := range names { for _, n := range names {
if n.Kind == "iconst" { if n.Kind == "iconst" || n.Kind == "uconst" {
fmt.Fprintf(&b, "\t%s,\n", n.C) fmt.Fprintf(&b, "\t%s,\n", n.C)
} else { } else {
fmt.Fprintf(&b, "\t0,\n") fmt.Fprintf(&b, "\t0,\n")
@ -564,6 +584,10 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
if i < len(ints) { if i < len(ints) {
n.Const = fmt.Sprintf("%#x", ints[i]) n.Const = fmt.Sprintf("%#x", ints[i])
} }
case "uconst":
if i < len(ints) {
n.Const = fmt.Sprintf("%#x", uint64(ints[i]))
}
case "fconst": case "fconst":
if i < len(floats) { if i < len(floats) {
n.Const = fmt.Sprintf("%f", floats[i]) n.Const = fmt.Sprintf("%f", floats[i])

View file

@ -88,7 +88,7 @@ type Name struct {
Mangle string // name used in generated Go Mangle string // name used in generated Go
C string // name used in C C string // name used in C
Define string // #define expansion 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 Type *Type // the type of xxx
FuncType *FuncType FuncType *FuncType
AddError bool AddError bool
@ -100,9 +100,9 @@ func (n *Name) IsVar() bool {
return n.Kind == "var" || n.Kind == "fpvar" 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 { 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. // A ExpFunc is an exported function, callable from C.