-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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/api: failed type-check with external test file #21181
Comments
If you remove the blank line, it is not a build tag anymore, but just a comment (package comment). |
Smells like some go/types issue. |
That's very weird. I don't see why go/types even cares about _test.go files in this case. Not a release blocker though. |
Thanks for tracking this down; your analysis so far is correct: Thus, the issue is only indirectly related to This is a caching bug and it disappears when Adding the Why they are used together I don't know yet. Will investigate. |
It doesn't matter if the added file is a test file or not; we can also add a file foo.go belonging to the package with the respective build tag. All that matters is the existence of the build tag. Simpler reproducer, after adding the foo_test.go file as described in the issue:
This only tests the API for 2 configurations: darwin-386 and windows-amd64. The problem is that we see two different versions of crypto/cipher. API checking a package requires that the package is imported (to get its API). The specifics are as follows. First, in darwin-386 context:
Second, in windows-amd64 context:
In x509, the struct rfc1423Algo contains the field cipherFunc func(key []byte) (cipher.Block, error) which refers to the directly imported cipher.Block. But on line 49, the value des.NewCipher imported via cipher/des is used. The two types, while identical, are from different packages objects (for the same package), and thus are not unified. As a result we get a type error during assignment. If crypto/des is excluded from package caching, the issue disappears. There are two package caches involved: The import cache that is used by importers (and cleared for each context), and the global package cache, keyed by package directory and tags. The import cache must ensure that each package (identified by package path) exists exactly once. When an object (in this case cipher.Block) is imported indirectly (via the import of crypto/des), the type cipher.Block must be unified with an already existing version of the type which is under the previously imported cipher package. Vice versa, package cipher is imported and an indirectly imported version of cipher.Block (and corresponding partial cipher package) already exists, that existing package structure must be used and extended. Because there's a second, external cache involved here things fall apart: When x509 imports des, the package cache hits and the des package is used (and added to the import package cache). But the cipher/des package contains types such as the signature of des.NewCipher which refers to cipher.Block which refers to the cipher package. That package is not added to the import package cache (step 3 above for the windows-amd64 context). Thus, when x509 imports cipher directly, it is added a 2nd time. Hence the bug. Possible solutions:
Probably not for 1.11. |
PS: Another option might be to make go/types not depend on the invariant that defined (*types.Named) types are represented uniquely. The implementation could choose structural comparison where pointer comparison fails. This would come at the cost of larger (duplicated) data structures and (minimally) slower type-checking, but only if a program makes use of it. (The structural comparison would kick in whenever pointer equivalence for such defined types fails, but in correct programs that would not happen.) cc: @alandonovan for comments. |
This would be a breaking change to the go/types API, since tons of client code depends on the canonicality of types.Named variables. The only correct solution on your list is the first one, to disable the global cache. Another correct (though more complicated) solution would be to extend the tagKey mechanism transitively so that you only get cache hits for packages that are transitively compatible. However, most packages depend on syscall or something that varies by build tags, so this would probably reduce the hit rate significantly. |
https://go-review.googlesource.com/c/go/+/125316 also triggers this |
Any update on this bug? |
@mengzhuo No. Does this affect you? |
@griesemer, it's causing TryBot failures for bizarre reasons on seemingly-unrelated CLs: https://go-review.googlesource.com/c/go/+/125316 |
Change https://golang.org/cl/138315 mentions this issue: |
What version of Go are you using (
go version
)?go version devel +835dfef Wed Jul 26 13:29:59 2017 +0000 linux/amd64
But also 1.8,1.7,1.6,1.5
What operating system and processor architecture are you using (
go env
)?GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/localdisk/itocar/gopath"
GORACE=""
GOROOT="/localdisk/itocar/golang"
GOTOOLDIR="/localdisk/itocar/golang/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build761245191=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
What did you do?
Added crypto/cipher/foo_test.go file to standard golang repo with following content
and ran all.bash
Also reproducible with go tool api crypto/x509
What did you expect to see?
all.bash passes and go tool api works without errors
What did you see instead?
go tool api crypto/x509
2017/07/26 15:49:38 error typechecking package crypto/x509: /localdisk/itocar/golang/src/crypto/x509/pem_decrypt.go:49:14: cannot use des.NewCipher (value of type func(key []byte) (crypto/cipher.Block, error)) as func(key []byte) (crypto/cipher.Block, error) value in struct literal (windows-amd64)
Interestingly if I remove blank line from cypto/cipher/foo_test.go
Everything works.
Replacing amd64 woth other build tags (e. g. freebsd) still leads to failure.
I've investigated a bit, and this is caused by crypto/cipher.Block actually being 2 different named types.
At least according to go/types/predicates.go:identical, but I have no Idea why unrelated file caused that.
The text was updated successfully, but these errors were encountered: