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

proposal: Go 2: NoCopy: official support in compiler (not just vet linter) #23764

Closed
ldemailly opened this issue Feb 10, 2018 · 19 comments
Closed
Labels
FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Milestone

Comments

@ldemailly
Copy link

ldemailly commented Feb 10, 2018

NoCopy official support in compiler (not just vet linter)

This is essentially a repeat of #8005 which got lost through FrozenDueToAge bot

(go versions not relevant - still an issue in go 1.9 and 1.10rc)

What did you do?

I wrote code that has a channel in a struct and got copied accidentally and then closed twice resulting in panic

What did you expect to see?

A way to detect accidental copies for objects that contain resources that need to stay unique
(or alternatively not getting panic when closing a channel more than once, but that's a different can of worm)

What did you see instead?

panic and time spent debugging until we found the trick

type noCopy struct{}
func (*noCopy) Lock() {}

type MyStruct struct {
  noCopy noCopy
  // ...

in #8005 (comment) (thanks for that!)

I believe every go project shouldn't rediscover and reimplement this and more importantly, one would want to get the detection at compile time instead of relying on a linter

while I'm here can I push my luck and ask for const ;-)

@ldemailly ldemailly changed the title NoCopy official support in compiler (not just vet linter) NoCopy: official support in compiler (not just vet linter) Feb 10, 2018
@bradfitz
Copy link
Contributor

Why don't I just reopen #8005 instead, which has much more context? Then we don't split the conversation.

@bradfitz
Copy link
Contributor

Oh, nevermind. #8005 was closed intentionally. I thought the vet fix had closed it via a "Fixes #nnn" line.

What's different now? Why should this stay open if #8005 was already closed?

@ldemailly
Copy link
Author

I would love #8005 to be reopened as far as I can tell there isn't a documented "NoCopy" (or comment tag) that exist and that the go compiler detects only the above workaround and the vet trick ?

@ianlancetaylor ianlancetaylor changed the title NoCopy: official support in compiler (not just vet linter) language: Go 2: NoCopy: official support in compiler (not just vet linter) Feb 10, 2018
@ianlancetaylor ianlancetaylor added LanguageChange v2 A language change or incompatible library change labels Feb 10, 2018
@ianlancetaylor ianlancetaylor added this to the Proposal milestone Feb 10, 2018
@ianlancetaylor ianlancetaylor changed the title language: Go 2: NoCopy: official support in compiler (not just vet linter) proposal: Go 2: NoCopy: official support in compiler (not just vet linter) Feb 10, 2018
@ianlancetaylor
Copy link
Contributor

As of 1.10 we run go vet for each use of go test, so it's not obvious to me that anything else is needed.

@ldemailly
Copy link
Author

Good to know, thanks ! so we just need noCopy -> NoCopy
(and hopefully people would have tests to go test ;-) )

@bcmills
Copy link
Contributor

bcmills commented Feb 12, 2018

Moving this check into the compiler would address a fairly important real-world use-case I have, for ensuring that Go wrappers of C++ classes are not copied in Go.

Building a project that mixes Go and C++ code from source in the same binary requires the use of a build tool other than just the go tool, which complicates any safety assumption that relies on that tool. (Specifically, go vet requires access to internal type information for the other packages, which isn't always available when using other build tools.)

@bcmills
Copy link
Contributor

bcmills commented Feb 12, 2018

(CC @alandonovan)

@josharian
Copy link
Contributor

Naive question: could cgo do that work?

@bcmills
Copy link
Contributor

bcmills commented Feb 12, 2018

cgo can do some of the work, but there's a big gap between that and “build C++ dependencies from source”.

The mixing-Go-with-C++ use case is just a concrete example. The more general problem is the assumption that vet works for verifying global properties of a program. It's great for strictly local properties, but anything more than that relies on assumptions about the build system that don't always hold.

@ianlancetaylor
Copy link
Contributor

There is no specific proposal here. This refers to #8005, which has a lot of different ideas but, as @rsc said, they are OK-but-not-obviously-right. We can consider a specific suggestion, but in the absence of that I am going to close this issue.

@ldemailly
Copy link
Author

@ianlancetaylor The specific proposal is a new "NoCopy" keyword or type (name doesn't matter) that would enforce at compile time that a Struct isn't copied - how it is implemented is a detail, what other specific details do you require?

@ianlancetaylor
Copy link
Contributor

Well, honestly, we need many more details. Is it a keyword or a type? If it is a type, where is it defined? If it is a keyword, what about the programs that currently use that keyword as an identifier? Where is the keyword/type used? What precisely does it mean? "Isn't copied" is vague; what exact operations are not permitted?

@ldemailly
Copy link
Author

It's probably easiest to define as a type, maybe similar to
https://golang.org/pkg/go/types/#Const
(and same package)

though a proper keyword could be nice (probably that would be nocopy)

what precisely does it mean: like c++ deletion of copy and assignment operator; you can't copy an instance only reference it

@ianlancetaylor
Copy link
Contributor

Please show an example of how this would be used.

I'm sorry to be pedantic, but you are suggesting changing the language. That has to be pedantic. It has to be precise. Go does not have copy or assignment operators, so referring to them doesn't help.

For example, given a value v which is somehow marked as nocopy, can I write

reflect.ValueOf(&v).Indirect().Interface()

? If not, what prevents it?

@ldemailly
Copy link
Author

ldemailly commented Apr 17, 2018

As a type:

type MyStruct struct {
	types.NoCopy
	field1 Foo
	// ...
}

ok := MyStruct{}
notok := ok # compile error
oktoo := &ok # that one is ok
// ... for function return etc

As a keywords it's more tricky on where/how that would be shoved in such as it make sense with existing language syntax, maybe

type MyStruct struct,nocopy { ...

Someone with knowledge of the internals would have to comment/suggest something for how reflect would work - what happens if you do the above with a Lock today ?

Again the idea is to get a strong compile time support, not a linter kind of support, though a standard type with linter is probably a low hanging fruit giving 99% of the benefits (which again are for people to not have to reivent the existing noCopy types in dozens of packages)

@davecheney
Copy link
Contributor

davecheney commented Apr 17, 2018 via email

@ldemailly
Copy link
Author

no *NoCopy would fail by definition

@davecheney
Copy link
Contributor

Would this be allowed?

func (m MyStuct) F() {
//
}

@ldemailly
Copy link
Author

ldemailly commented Apr 18, 2018

no would have to be func (m *MyStuct) F() { and func NewMyStruct() *MyStruct
which is part of the benefit, not accidental, even if optimized out, copies

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Projects
None yet
Development

No branches or pull requests

7 participants