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

toolchain: Minimal version selection applies to indirect dependencies even when they are module-local #69798

Closed
pierreprinetti opened this issue Oct 7, 2024 · 14 comments

Comments

@pierreprinetti
Copy link
Contributor

pierreprinetti commented Oct 7, 2024

Go version

go version go1.23.2 linux/amd64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOBIN='/var/home/pierre/code/bin'
GOCACHE='/var/home/pierre/.cache/go-build'
GOENV='/var/home/pierre/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/var/home/pierre/code/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/var/home/pierre/code'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/var/home/pierre/sdk/go1.23.2'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/var/home/pierre/sdk/go1.23.2/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.23.2'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='/var/home/pierre/.config/go/telemetry'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3187542746=/tmp/go-build -gno-record-gcc-switches'

What did you do?

The background

While preparing a new version of my library, I am testing the interactions between the old version (v2) and the new version I am working on (v3). I want to import them both, using local rewrites in my local go.mod. The caveat: there is no v3 yet, so internally the imports of both versions are identical.

The actual issue

This is the most minimal reproducer I could produce: https://go.dev/play/p/Up68jxuX6_v

This might help understanding the directory structure of the example in the sandbox:

$ tree
.
├── go.mod
├── main.go
├── new
│   ├── go.mod
│   ├── speak.go
│   └── sub
│       └── say.go
└── old
    ├── go.mod
    ├── speak.go
    └── sub
        └── say.go

5 directories, 8 files

There is a package main that imports both old and new. Both old and new contain exactly the same code, and they both import a sub-package sub. The content of those two sub packages is different: one says I am old, the other says I am new.

Both imports are resolved to old/sub.

I find it surprising that imports within a module are not rewritten when a module is imported as a rewrite. I expected package_new.Speak() to print I am new, because package_new imports sub from within its own module, and I have rewritten the import of that module.

What did you see happen?

$ go run .
sub says: "I am old"
sub says: "I am old"

What did you expect to see?

$ go run .
sub says: "I am old"
sub says: "I am new"
@pierreprinetti pierreprinetti changed the title toolchain: Minimal version selector applies to indirect dependencies even when they are in the same module toolchain: Minimal version selection applies to indirect dependencies even when they are in the same module Oct 7, 2024
@pierreprinetti pierreprinetti changed the title toolchain: Minimal version selection applies to indirect dependencies even when they are in the same module toolchain: Minimal version selection applies to indirect dependencies even when they are module-local Oct 7, 2024
@seankhliao
Copy link
Member

this looks working as intended, MVS applies to modules and not packages. modules need to declare different names in their go.mod

Unlike many projects, the Go project does not use GitHub Issues for general discussion or asking questions. GitHub Issues are used for tracking bugs and proposals only.

For questions please refer to https://github.com/golang/go/wiki/Questions

@seankhliao seankhliao closed this as not planned Won't fix, can't repro, duplicate, stale Oct 7, 2024
@pierreprinetti
Copy link
Contributor Author

pierreprinetti commented Oct 7, 2024

MVS applies to modules and not packages

hmm right, that's why I am surprised that it appears to apply to packages in this case, and that looks as a bug to me.

@pierreprinetti
Copy link
Contributor Author

pierreprinetti commented Oct 7, 2024

EDIT: I am removing the reference to internal packages, because it's misleading.

@pierreprinetti
Copy link
Contributor Author

@seankhliao

I have slightly changed the example to highlight the suspicious behaviour.

The module is correctly fetched, but subpackages of the modules are fetched by name without namespacing them to the module.

Reproducer: https://go.dev/play/p/1OD7Zm41iMM

$ go run .
from OLD, subpackage says: "I am old"
from NEW, subpackage says: "I am old"

Whereas it should have been:

$ go run .
from OLD, subpackage says: "I am old"
from NEW, subpackage says: "I am new"

The modules aliased as "old" and "new" are correctly fetched, but the subpackage of "NEW " should have said "I am new". Instead, the sub subpackage of OLD is used.

Are you sure that mixing packages from different modules really is the intended behaviour? That is surprising enough to look like a bug to me.

@seankhliao
Copy link
Member

seankhliao commented Oct 7, 2024

They both import the same package github.com/golang/speak/sub, only one package is named that.

replace directives do not allow you to rename an entire module, it only changes the source code location.

@zigo101
Copy link

zigo101 commented Oct 7, 2024

... modules need to declare different names in their go.mod

If the module name of new is changed to a different name other than github.com/golang/speak-new, then the code will not compile.

@zigo101
Copy link

zigo101 commented Oct 7, 2024

If the module name of new is changed to a different name, then the code will not compile.

Why it doesn't compile is probably related to #50278

@zigo101
Copy link

zigo101 commented Oct 7, 2024

From the perspective of the module in the new folder, the github.com/golang/speak/sub package path is ./new/sub. But when it is used as a dependency module, the github.com/golang/speak/sub package path is ./old/sub. This is totally unexpected from the perspective of the module in the new folder and it is completely outside of the consciousness of the maintainer of the module in the new folder.

In my honest opinion, the compiler should report a duplicated packages alike error (or at least a vet rule). The current behavior (it compiles) is very confusing/surprising. (I totally understand OP's feeling here.)

(This is just one pitfall of the current module system design. There are some others.)

@zigo101
Copy link

zigo101 commented Oct 7, 2024

The code (https://go.dev/play/p/VLhjzQtcbD3) works as OP's intention in an unexpected way. (It proves that how the modules are named in the go.mod files in the replacement folders is not important. They can be named as anything and the compiler will totally ignore them.)

@zigo101
Copy link

zigo101 commented Oct 7, 2024

This issue proves that relative import paths are necessary in the module system.

@zigo101
Copy link

zigo101 commented Oct 7, 2024

I suggest to re-open this issue, with a different title: a dependency module A should not use the packages in another dependency module B if A's go.mod file doesn't require B.

@seankhliao
Copy link
Member

go.mod files may be incomplete, for example from before indirect dependencies were added to go.mod

@zigo101
Copy link

zigo101 commented Oct 8, 2024

What you described and I described are two things. In the OP's case, speak-new doesn't require speak at all, even indirectly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants