Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/cgo: compiler changes in constant warnings may require cgo changes #26066

Closed
ianlancetaylor opened this issue Jun 26, 2018 · 7 comments

Comments

@ianlancetaylor
Copy link
Contributor

commented Jun 26, 2018

Test case:

package main

import "fmt"

/*
const unsigned long long int neg = (const unsigned long long) -1;
*/
import "C"

func main() {
	var i int64	
	i = int64(C.neg)
	fmt.Println(C.neg)	
	fmt.Println(i)
}

Building that test case with GCC versions before GCC 8 works. With GCC 8 and later, it gets

# command-line-arguments
../../foo8.go:12:6: error: integer constant overflow
  i = int64(C.neg)
      ^

What has changed is the handling of this C program, similar to that generated by cgo.

const unsigned long long int neg = (const unsigned long long) -1;
void f1(void) { enum { x = (neg)*1 }; }
void f2(void) { static const double x = (neg); }

Before GCC 8, compiling this program gets

foo.c: In function ‘f1’:
foo.c:2:24: error: enumerator value for ‘x’ is not an integer constant
 void f1(void) { enum { x = (neg)*1 }; }
                        ^
foo.c: In function ‘f2’:
foo.c:3:41: error: initializer element is not constant
 void f2(void) { static const double x = (neg); }
                                         ^

In GCC 8 and later, it gets

foo.c: In function ‘f1’:
foo.c:2:24: error: enumerator value for ‘x’ is not an integer constant
 void f1(void) { enum { x = (neg)*1 }; }
                        ^

In other words, static const double x = (neg); is now accepted without warning. This appears to be due to the fix for https://gcc.gnu.org/PR69960. See also https://gcc.gnu.org/PR83222 and https://gcc.gnu.org/PR86289.

This effect of this warning change on cgo is that in the original test case neg now appears to be a floating point constant. cgo represents this constant as 18446744073709551616.000000. This leads to the integer constant overflow error shown above.

We're going to need to figure this out.

@gopherbot

This comment has been minimized.

Copy link

commented Jun 26, 2018

Change https://golang.org/cl/121035 mentions this issue: cmd/cgo: handle GCC 8 change in errors about constant initializers

@kyoukim

This comment has been minimized.

Copy link

commented Jun 26, 2018

I have filed a bug in the gcc bugzilla, thinking it might be a gccgo issue:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86289

With gc, I could not reproduce it. However, I realized that it was just becauge gc does not invoke the brand-new gcc in my environment.

I have spent quite a bit time on this issue. I am willing to work on this. Regardless of that, here are what I have learnt.

Firstly, it looks like gc and gccgo with gcc 7.x was working. However, I am not sure if I could call it really working. The basic idea would be a 64 bit integer "neg" should be regarded as an integral value rather than a floating point value.

What had happened is the 64 bit integer was "not-int-const" AND "not-num-const." Here are the code that cgo gives to gcc to figure out "neg":

#line 1 "not-int-const"
void __cgo_f_1_3(void) { enum { __cgo_undefined__3 = (neg)*1 }; }

That is going to be a compile error as underlying enum type is likely 32 bits. However, look another piece of code that cgo generates:

const unsigned long long int neg = (const unsigned long long) -1;`
#line 1 "cgo-generated-wrapper"
#line 1 "cgo-dwarf-inference"
__typeof__(neg) *__cgo__0;
long long __cgodebug_ints[] = {
	0,
	1
};
double __cgodebug_floats[] = {
	neg,
	1
};

Here, the value for integrals seems long long. Thus, for the first place, I am wondering why neg falls into "not-int-const."

Moreover, the reason why neg is "not-num-const" is a bit tricky. C11 standard, as far as I know it, may say that a 'const' variable is not a constant which can be used to initialize a local 'static const' variable. However, it seems that the compiler is allowed to extend the range of constantness in the context.

For me, it seems like cgo relies on gcc regarding what gcc is allowed not to be consistent. Therefore, gcc 7.3 says "not-num-const" according to the C11 standard (even though the given flag might be gnu11) but gcc 8+ silently sort of optimized & compiles the same piece of code with the same -std option.

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor Author

commented Jun 26, 2018

@kyoukim Thanks. As noted above, I've sent https://golang.org/cl/121035 to fix this issue. Want to take a look at that approach?

@kyoukim

This comment has been minimized.

Copy link

commented Jun 27, 2018

@ianlancetaylor Thank you so much! It makes perfect sense to me. It is blocking 64 bits integers to be regarded as a constant. I am relatively new to the code base, and still wondering why a 64 bits integer is treated differently from a 32 bits integer.

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor Author

commented Jun 27, 2018

I'm not sure about 32-bits vs. 64-bits; what difference are you seeing?

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor Author

commented Jun 27, 2018

Thanks for looking at the change, by the way.

@kyoukim

This comment has been minimized.

Copy link

commented Jun 27, 2018

I thought a 32 bits int would not fall into not-int-const. It was my bad. An int32 indeed does not look being differently treated. Thank you!

@gopherbot gopherbot closed this in da76981 Jun 29, 2018

@golang golang locked and limited conversation to collaborators Jun 29, 2019

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
3 participants
You can’t perform that action at this time.