go: reflect: do not deprecate Ptr APIs just yet
In basically all of my modules, I support the two latest major Go versions - right now, this means 1.16 and 1.17. I develop on Go master, for the sake of helping with testing and developing new features.
In one of the codebases that must still support Go 1.16, I got the following warning from staticcheck:
$ staticcheck ./...
cmd/shfmt/json.go:27:7: reflect.Ptr is deprecated: use the new spelling, Pointer. (SA1019)
syntax/walk.go:269:7: reflect.Ptr is deprecated: use the new spelling, Pointer. (SA1019)
Staticcheck is right: the type is marked as deprecated.
Unfortunately for me, this is a warning that will be bothering me for six to twelve months before I can fix it.
reflect.Pointer won’t appear until Go 1.18, and I won’t drop support for 1.17 until 1.19 comes out.
I think the standard library needs to at least wait one major release before formally deprecating APIs.
That is, the earliest we could deprecate reflect.Ptr would be in Go 1.19, when the majority of module authors can stop worrying about Go 1.17 and older.
Alternatively, to be extra conservative towards module authors who move slowly, we could make the general rule to wait two major releases.
I thought this rule was already common knowledge, as https://go-review.googlesource.com/c/go/+/284777/ was rejected for deprecating io/ioutil too early.
We seem to have dealt with the reflect API changes differently, though.
Perhaps this means we should write down this general standard library deprecation rule?
Marking as NeedsDecision for 1.18, as making a decision after 1.18 would be a bit late 😃
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 5
- Comments: 29 (27 by maintainers)
Commits related to this issue
- all: use reflect.{Pointer,PointerTo} Updates #47651 Updates #48665 Change-Id: I69a87b45a5cad7a07fbd855040cd9935cf874554 Reviewed-on: https://go-review.googlesource.com/c/go/+/358454 Trust: Cuong Man... — committed to golang/go by cuonglm 3 years ago
- add a note about deprecating at Go 1.N+2 for "forwarding" deprecations - see https://github.com/golang/go/issues/48665 — committed to zchee/golang-wiki by mvdan 3 years ago
Deprecated does not imply the code isn’t valid. If a tool is suggesting that, it’s a problem with the tool and not with the deprecation mark. Deprecated just means it’s no longer the recommended way to do something.
We should remove the // Deprecated here. We don’t want tools to complain about perfectly valid code.
FWIW, Staticcheck maintains a handwritten mapping of Go identifiers to 1) when they were deprecated 2) when their recommended alternative became available. As such, Staticcheck won’t flag uses of a deprecated identifier if the user is targeting an older version of Go.
We usually update that mapping close to a Go release (either closely before or closely after…) and make a new release.
Generally speaking, Go has always immediately deprecated things when alternatives became available. io/ioutil is the odd one out here.
Now I’m curious what makes ioutil special so that we should wait a few releases to deprecate it.
Thanks all for the input and solution. Just to double check - are we ending up with clear guidelines on how and when standard library APIs should be deprecated? I would hope so, and that they would be documented somewhere, so we don’t end up here again next cycle 😃
Fair enough, but we don’t want people to start replacing
PtrbyPointeruntilPointeris available in all supported versions. So I guess the correct approach is to only mark an identifier as deprecated if the replacement is available in all supported versions of Go.Staticcheck actually does do that for some deprecated objects, such as insecure cryptographic functions. These will get flagged regardless of the version in which they were deprecated. These are the objects marked with
DeprecatedNeverUsein https://github.com/dominikh/go-tools/blob/8b9edfdb7a54d16418dda13a02fe1864c34624c4/knowledge/deprecated.go.You’re right that
go 1.Ndoesn’t mean that only >=1.N is supported. That’s especially true because the version specified has to be at least as high as the highest version required by any package or (build-tagged) file in the module. But I don’t think that assuming 1.N-1 improves on that. Defaulting to the version specified ingo.modis a guess, but it’s an easy to understand one. I’d rather not add an offset to the value.Probably related: #30791.
In Staticcheck, we make this the user’s responsibility. Instead of us subtracting 2 from the determined version, we expect the user to either use the desired version in their
go.mod– which works for most simpler modules that target a single minimum version across all files – or to use the-goflag to overwrite the targeted version.This will probably become much less of a concern once Go drops support for GOPATH, as all packages will have a
go.modfile, and we get the version in that fromgo list(which means we also get its fallbacks.)Thinking out loud.
If an identifier is deprecated because it is unsafe or dangerous, then we want that deprecation warning to be issued in all cases.
If an identifier is deprecated because we have a newer, cooler, replacement, and the replacement is first available in Go 1.N, then it seems to me that the warning should be issued if compiling for Go 1.N+2. This is because when we are targeting Go 1.N+2, then Go 1.N is no longer supported and it’s not so important to ensure that things still work for people using it.
This is because writing "go 1.N’ in a go.mod file does not mean that only go 1.N is supported. It means that we should use Go 1.N semantics, but in general it may still be OK to build with Go 1.N-1.
I’m not sure how to make that work with staticcheck.
Separately, perhaps if staticcheck can’t get the Go version from a go.mod file, it should use 1.16 (or perhaps 1.11), as that is what cmd/go does (https://go.googlesource.com/go/+/refs/heads/master/src/cmd/go/internal/modload/modfile.go#84).
Here is how Staticcheck handles deprecation, based on an example:
Feel free to tell me if you disagree with that behavior.