go/internal/gcimporter: ensure that imported floats are of float kind

As of CL 290630, go/types enforces some bitsize limits on integer const
values. We need to be careful to preserve the constant Kind when
importing from gc export data.

Ideally we could follow CL 288632, but the go/constant APIs used in that
CL are not available to x/tools, which (at least for the moment) must
still build at go1.12. Instead, simply call constant.ToFloat to force
the Kind.

Add a test for the imported Kind. This test should probably also be
upstreamed to std/go/internal/gcimporter, now that go/types relies on
this behavior.

Change-Id: I84b4b15b398962305aa6e3e9e12ede6a7373230b
Reviewed-on: https://go-review.googlesource.com/c/tools/+/307590
Trust: Robert Findley <rfindley@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
diff --git a/go/internal/gcimporter/gcimporter_test.go b/go/internal/gcimporter/gcimporter_test.go
index b517bf1..cfafbbf 100644
--- a/go/internal/gcimporter/gcimporter_test.go
+++ b/go/internal/gcimporter/gcimporter_test.go
@@ -10,6 +10,7 @@
 import (
 	"bytes"
 	"fmt"
+	"go/constant"
 	"go/types"
 	"io/ioutil"
 	"os"
@@ -320,26 +321,11 @@
 	}
 
 	for _, test := range importedObjectTests {
-		s := strings.Split(test.name, ".")
-		if len(s) != 2 {
-			t.Fatal("inconsistent test data")
-		}
-		importPath := s[0]
-		objName := s[1]
-
-		pkg, err := Import(make(map[string]*types.Package), importPath, ".", nil)
-		if err != nil {
-			t.Error(err)
-			continue
-		}
-
-		obj := pkg.Scope().Lookup(objName)
+		obj := importObject(t, test.name)
 		if obj == nil {
-			t.Errorf("%s: object not found", test.name)
-			continue
+			continue // error reported elsewhere
 		}
-
-		got := types.ObjectString(obj, types.RelativeTo(pkg))
+		got := types.ObjectString(obj, types.RelativeTo(obj.Pkg()))
 		if got != test.want {
 			t.Errorf("%s: got %q; want %q", test.name, got, test.want)
 		}
@@ -350,6 +336,54 @@
 	}
 }
 
+func TestImportedConsts(t *testing.T) {
+	testenv.NeedsGo1Point(t, 11)
+	skipSpecialPlatforms(t)
+
+	tests := []struct {
+		name string
+		want constant.Kind
+	}{
+		{"math.Pi", constant.Float},
+		{"math.MaxFloat64", constant.Float},
+		{"math.MaxInt64", constant.Int},
+	}
+
+	for _, test := range tests {
+		obj := importObject(t, test.name)
+		if got := obj.(*types.Const).Val().Kind(); got != test.want {
+			t.Errorf("%s: imported as constant.Kind(%v), want constant.Kind(%v)", test.name, got, test.want)
+		}
+	}
+}
+
+// importObject imports the object specified by a name of the form
+// <import path>.<object name>, e.g. go/types.Type.
+//
+// If any errors occur they are reported via t and the resulting object will
+// be nil.
+func importObject(t *testing.T, name string) types.Object {
+	s := strings.Split(name, ".")
+	if len(s) != 2 {
+		t.Fatal("inconsistent test data")
+	}
+	importPath := s[0]
+	objName := s[1]
+
+	pkg, err := Import(make(map[string]*types.Package), importPath, ".", nil)
+	if err != nil {
+		t.Error(err)
+		return nil
+	}
+
+	obj := pkg.Scope().Lookup(objName)
+	if obj == nil {
+		t.Errorf("%s: object not found", name)
+		return nil
+	}
+	return obj
+}
+
 // verifyInterfaceMethodRecvs verifies that method receiver types
 // are named if the methods belong to a named interface type.
 func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
diff --git a/go/internal/gcimporter/iimport.go b/go/internal/gcimporter/iimport.go
index b236deb..8ed8bc6 100644
--- a/go/internal/gcimporter/iimport.go
+++ b/go/internal/gcimporter/iimport.go
@@ -473,6 +473,14 @@
 	switch {
 	case exp > 0:
 		x = constant.Shift(x, token.SHL, uint(exp))
+		// Ensure that the imported Kind is Float, else this constant may run into
+		// bitsize limits on overlarge integers. Eventually we can instead adopt
+		// the approach of CL 288632, but that CL relies on go/constant APIs that
+		// were introduced in go1.13.
+		//
+		// TODO(rFindley): sync the logic here with tip Go once we no longer
+		// support go1.12.
+		x = constant.ToFloat(x)
 	case exp < 0:
 		d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp))
 		x = constant.BinaryOp(x, token.QUO, d)