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

go/constant: cannot deal with exponential notation #43165

Closed
aykevl opened this issue Dec 13, 2020 · 4 comments
Closed

go/constant: cannot deal with exponential notation #43165

aykevl opened this issue Dec 13, 2020 · 4 comments
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@aykevl
Copy link

aykevl commented Dec 13, 2020

What version of Go are you using (go version)?

$ go version
go version go1.15.5 linux/amd64

Does this issue reproduce with the latest release?

Yes, at least with Go 1.15.5.

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/ayke/.cache/go-build"
GOENV="/home/ayke/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/ayke/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/ayke:/home/ayke"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/ayke/tmp/constrepro/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build759953502=/tmp/go-build -gno-record-gcc-switches"

What did you do?

There is no easy way of reproducing this, so I made a new repository with a reproducer in code form: https://github.com/aykevl/constrepro

After cloning it, try running it:

go run . ./const

What did you expect to see?

I expected the following output:

instruction: 5:uint32 / 20:uint32
value: 20:uint32
result: 20 true

You can get this if you use the other twenty constant in ./const/const.go (see comment).

What did you see instead?

instruction: 5:uint32 / 20:uint32
value: 20:uint32
panic: 20 not an Int

goroutine 1 [running]:
go/constant.Uint64Val(0x7194e0, 0xc000231540, 0xc0001bde60, 0x2)
	/usr/local/go/src/go/constant/value.go:484 +0x189
main.main()
	/home/ayke/tmp/constrepro/main.go:37 +0x231
exit status 2

Clearly, 20 is an int. But somewhere it seems to have been converted to a float. You can see this if you inspect the generated AST (see line 40):

    23  .  .  .  Specs: []ast.Spec (len = 1) {
    24  .  .  .  .  0: *ast.ValueSpec {
    25  .  .  .  .  .  Names: []*ast.Ident (len = 1) {
    26  .  .  .  .  .  .  0: *ast.Ident {
    27  .  .  .  .  .  .  .  NamePos: /home/ayke/tmp/constrepro/const/const.go:6:2
    28  .  .  .  .  .  .  .  Name: "twenty"
    29  .  .  .  .  .  .  .  Obj: *ast.Object {
    30  .  .  .  .  .  .  .  .  Kind: const
    31  .  .  .  .  .  .  .  .  Name: "twenty"
    32  .  .  .  .  .  .  .  .  Decl: *(obj @ 24)
    33  .  .  .  .  .  .  .  .  Data: 0
    34  .  .  .  .  .  .  .  }
    35  .  .  .  .  .  .  }
    36  .  .  .  .  .  }
    37  .  .  .  .  .  Values: []ast.Expr (len = 1) {
    38  .  .  .  .  .  .  0: *ast.BasicLit {
    39  .  .  .  .  .  .  .  ValuePos: /home/ayke/tmp/constrepro/const/const.go:6:11
    40  .  .  .  .  .  .  .  Kind: FLOAT
    41  .  .  .  .  .  .  .  Value: "2e1"
    42  .  .  .  .  .  .  }
    43  .  .  .  .  .  }
    44  .  .  .  .  }
    45  .  .  .  }

So somehow the value is interpreted as an integer by the SSA package, but in the AST it is somehow considered a float. I think the AST here is wrong (or maybe constant.Uint64Val), as the code typechecks just fine and is accepted by the Go toolchain.

(This is the bug I was referring to at the bottom of #43163).

@aykevl
Copy link
Author

aykevl commented Dec 14, 2020

I found another interesting case, for this piece of code:

func main() {
	x := uint64(5)
	println(x / (12e9 / 1))
}

It results in the following output:

instruction: 5:uint64 / 1.2e+10:uint64
value: 1.2e+10:uint64
panic: 1.2e+10 not an Int

goroutine 1 [running]:
go/constant.Uint64Val(0x7194e0, 0xc00009b9c0, 0xc0000c3e60, 0x2)
	/usr/local/go/src/go/constant/value.go:484 +0x189
main.main()
	/home/ayke/tmp/constrepro/main.go:37 +0x231
exit status 2

In this case, 1.2e+10 is still an integer but somehow got displayed with scientific notation, and thus with a floating point.

(Reproduce by replacing the main in const/const.go of my reproducer repository).

@toothrot toothrot added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Dec 17, 2020
@toothrot toothrot added this to the Backlog milestone Dec 17, 2020
@toothrot
Copy link
Contributor

I could be mistaken, but I believe exponent notation is only for floating-point literals: https://golang.org/ref/spec#Floating-point_literals

/cc @griesemer

@griesemer
Copy link
Contributor

As @toothrot has already pointed out, 2e1 is a floating-point constant. It is untyped which means that in this piece of code it can be freely used in an operation with x which is of uint32 type, but only because 2e1 can be represented as an integer (20) without any loss of precision.

The constant package doesn't care about types. It cares only about value representation. 2e0 is of kind FLOAT (line 40 of the ast printout). The type-checker may give it a type, in this case uint32 eventually, because it can be represented as such a value. But because the kind is FLOAT you cannot use constant.Uint64Val. Note that the ssa.Value printed does say that the type is uint32 as expected.

You can explicitly convert the value: n, exact := constant.Uint64Val(constant.ToInt(value.Value))

This is working as expected.

@aykevl
Copy link
Author

aykevl commented Dec 17, 2020

Thank you for the explanation! I have tried constant.ToInt and it solves my problem in the reproducer.
My assumption was that I could feed Value from *ssa.Const directly to constant.Uint64Val. Apparently this worked in almost all cases, but not in these few edge cases. I will fix my code to let the value go through constant.ToInt first.

aykevl added a commit to tinygo-org/tinygo that referenced this issue Dec 23, 2020
Before this change, the compiler could panic with the following message:

    panic: 20 not an Int

That of course doesn't make much sense. But it apparently is expected
behavior, see golang/go#43165 for details.

This commit fixes this issue by converting the constant to an integer if
needed.
aykevl added a commit to tinygo-org/tinygo that referenced this issue Dec 23, 2020
Before this change, the compiler could panic with the following message:

    panic: 20 not an Int

That of course doesn't make much sense. But it apparently is expected
behavior, see golang/go#43165 for details.

This commit fixes this issue by converting the constant to an integer if
needed.
aykevl added a commit to tinygo-org/tinygo that referenced this issue Dec 24, 2020
Before this change, the compiler could panic with the following message:

    panic: 20 not an Int

That of course doesn't make much sense. But it apparently is expected
behavior, see golang/go#43165 for details.

This commit fixes this issue by converting the constant to an integer if
needed.
deadprogram pushed a commit to tinygo-org/tinygo that referenced this issue Dec 27, 2020
Before this change, the compiler could panic with the following message:

    panic: 20 not an Int

That of course doesn't make much sense. But it apparently is expected
behavior, see golang/go#43165 for details.

This commit fixes this issue by converting the constant to an integer if
needed.
@golang golang locked and limited conversation to collaborators Dec 17, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

4 participants