-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
reflect: StructOf doesn't generate wrapper methods for embedded fields #15924
Comments
This is a known deficiency, but it should be documented (or we should unexport StructOf until it is fixed). CC @sbinet |
let me send a CL that documents this temporary (ie: go-1.7) limitation. |
CL https://golang.org/cl/23681 mentions this issue. |
This CL documents that StructOf currently does not generate wrapper methods for embedded fields. Updates golang#15924 Change-Id: I932011b1491d68767709559f515f699c04ce70d4 Reviewed-on: https://go-review.googlesource.com/23681 Reviewed-by: David Crawshaw <[email protected]>
cc me. I had a prototype of this that I didn't send out in the 1.7 cycle, hopefully I can get to it in 1.8. |
Too late. |
Requires creating methods at run time, which is discussed in #16522. |
Dup with more repros in #20824 |
CL https://golang.org/cl/47035 mentions this issue. |
When StructOf is used with an anonymous field that has methods, and that anonymous field is not the first field, the methods we generate are incorrect because they do not offset to the field as required. If we encounter that case, panic rather than doing the wrong thing. Fixes #20824 Updates #15924 Change-Id: I3b0901ddbc6d58af5f7e84660b5e3085a431035d Reviewed-on: https://go-review.googlesource.com/47035 Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
FYI, as of CL 100846, methods are now sorted with exported names before non-exported names, and there's an extra field to track the number of exported methods. The current code works because it only allows exported methods from one type, so they're already in the same sort order, and we don't need to worry about counting how many exported methods there are. If either of those constraints change, we'll need to change the code (e.g., to sort methods and/or count the exported methods). I've left TODOs to mark where I expect this needs to be done. Also, one other comment: currently, StructOf only allows promoting methods from the first field, but it could be generalized slightly to allowing promoting methods of any field with offset==0. This would allow methods from multiple zero-width types to be promoted. |
Change https://golang.org/cl/121316 mentions this issue: |
The current implementation does not generate wrappers for methods of embedded non-interface types. We can only skip the wrapper if kindDirectIface of the generated struct type matches kindDirectIface of the embedded type. Panic if that is not the case. It would be better to actually generate wrappers, but that can be done later. Updates #15924 Fixes #24782 Change-Id: I01f5c76d9a07f44e1b04861bfe9f9916a04e65ca Reviewed-on: https://go-review.googlesource.com/121316 Reviewed-by: Brad Fitzpatrick <[email protected]>
while implementing Alas, it seems not. The following example crashes with SIGSEGV on both my local Go 1.11.4 installation and on playground https://play.golang.org/p/jny1YKZsHlp
on my local Go 1.11.4, the output is:
on playground https://play.golang.org/p/jny1YKZsHlp the output is instead:
I would expect that either |
83092a4#diff-cdbb65f700222dd514ff85e972c88be2R2470 broke my program after I've updated the Go version. I didn't use any of the the struct methods. The struct was built only to create dynamic data structures matching a specific format when encoded with various encoders such encoding/json. |
@mihaiav I'm sorry that happened but given the choice between "incorrectly claim we did what the program requested" and "give an error" I think it's better to give an error. You should be able to fix your program by making a regular field rather than an embedded field. |
I was reading https://groups.google.com/forum/#!topic/golang-nuts/e9ij7B_c5DE and thought this interesting, so I wrote some test cases: Seems like if We cannot call the method from the embedded type. |
When generating an interface wrapper, lookup existing wrappers by method to get the one with the biggest set of methods implemented by interpreter. A string method is also added to wrappers, in order to provide a string representation of the interpreter value rather than the wrapper itself (at least for %s and %v verbs). This allows the runtime to pickup an interpreter method automatically even if the conversion to the interface is not specified in the script. As in Go spec, it is enough for the type to implement the required methods. A current limitation is that only single wrappers can be instantiated, not allowing to compose interfaces. This limitation can be removed when the Go reflect issue golang/go#15924 is fixed. Fixes #435.
* fix: make interpreter methods discoverable by runtime When generating an interface wrapper, lookup existing wrappers by method to get the one with the biggest set of methods implemented by interpreter. A string method is also added to wrappers, in order to provide a string representation of the interpreter value rather than the wrapper itself (at least for %s and %v verbs). This allows the runtime to pickup an interpreter method automatically even if the conversion to the interface is not specified in the script. As in Go spec, it is enough for the type to implement the required methods. A current limitation is that only single wrappers can be instantiated, not allowing to compose interfaces. This limitation can be removed when the Go reflect issue golang/go#15924 is fixed. Fixes #435. * test: add a simpler test
i meet the same question, how to solve it at last ? |
I am not aware of any general mechanism to create methods at runtime - if I remember correctly, it would require code generation. The function And that's not the only difficulty that |
This change implements a workaround to better support composed interfaces in yaegi and let the interpreter to define objects which impletment multiple interfaces at once. We use the existing MapTypes to store what possible composed interfaces wrapper could be used for some interfaces. When generating an interface wrapper, the wrapper with the highest number of implemented methods is chosen. This is still an imperfect solution but it improves the accuracy of interpreter in some critical cases. This workaround could be removed in future if/when golang/go#15924 is resolved. Fixes traefik#1425.
This change implements a workaround to better support composed interfaces in yaegi and let the interpreter define objects which implement multiple interfaces at once. We use the existing MapTypes to store what possible composed interface wrapper could be used for some interfaces. When generating an interface wrapper, the wrapper with the highest number of implemented methods is chosen. This is still an imperfect solution but it improves the accuracy of interpreter in some critical cases. This workaround could be removed in future if/when golang/go#15924 is resolved. Fixes #1425.
Only structures with one embedded field can be marked anonymous, due to golang/go#15924. Also check only that the method is defined, do not verify its complete signature, as the receiver may or not be defined in the arguments of the method. Fixes traefik#1537.
Only structures with one embedded field can be marked anonymous, due to golang/go#15924. Also check only that the method is defined, do not verify its complete signature, as the receiver may or not be defined in the arguments of the method. Fixes #1537.
…o present all the pieces at the same time. This PR creates a system for supporting YAML files using different `api_version`s from a single binary. This is implemented by having a separate set of structs for each `(kind,api_version)` pair. Each api_version can change the semantics or existence of YAML fields. The structs are stored in `templates/model/$KIND/$APIVERSION`. Instructions for adding a new `api_version` are in `templates/model/README.md`, that might be a good starting point for review. Old versions are automatically converted to new versions before being used: old structs define an Upgrade() method that define how to upgrade them. There's a registry of `api_version`s in decode.go that defines which versions exist and which structs are associated with each. To demonstrate that this all actually works, this PR creates a new API version `v1beta1`. The only change in this new version is adding the optional `if` field for each step in spec.yaml (#220). Alternatives considered to try to avoid this huge copy-pasting of structs: - Struct embedding to reduce copy-pasta: this would expose a more complicated API to the rest of the code that has to use the structs. It makes creating a new version more painful. It also triggers golang/go#15924 since we use `reflect.StructOf`. - Use github.com/mitchellh/mapstructure` to avoid so much double-parsing: we'd lose YAML position information, and our users really appreciate getting line numbers on their error messages. - Use `switch(apiVersion)` within a single struct instead of copy-pasting structs: high risk of accidentally changing the behavior of old API versions and generally creating fragile code. I've marked files that are mostly copy-pasted so you can mostly skip reviewing them
Apologies for the large PR, but I think in this case it's important to present all the pieces at the same time. This PR creates a system for supporting YAML files using different `api_version`s from a single binary. This is implemented by having a separate set of structs for each `(kind,api_version)` pair. Each api_version can change the semantics or existence of YAML fields. The structs are stored in `templates/model/$KIND/$APIVERSION`. Instructions for adding a new `api_version` are in `templates/model/README.md`, that might be a good starting point for review. Old versions are automatically converted to new versions before being used: old structs define an Upgrade() method that define how to upgrade them. There's a registry of `api_version`s in decode.go that defines which versions exist and which structs are associated with each. To demonstrate that this all actually works, this PR creates a new API version `v1beta1`. The only change in this new version is adding the optional `if` field for each step in spec.yaml (#220). Alternatives considered to try to avoid this huge copy-pasting of structs: - Struct embedding to reduce copy-pasta: this would expose a more complicated API to the rest of the code that has to use the structs. It makes creating a new version more painful. It also triggers golang/go#15924 since we use `reflect.StructOf`. - Use http://github.com/mitchellh/mapstructure to avoid so much double-parsing: we'd lose YAML position information, and our users really appreciate getting line numbers on their error messages. - Use `switch(apiVersion)` within a single struct instead of copy-pasting structs: high risk of accidentally changing the behavior of old API versions and generally creating fragile code. I've marked files that are mostly copy-pasted so you can mostly skip reviewing them
A wrapper struct with one embedded member that has methods behaves differently if defined at compile time vs constructed at runtime. In particular, methods defined on the inner struct sometimes cannot be found with This playground demonstrates: https://go.dev/play/p/lDE49fZuk5N
In a debug session I can see that |
I expected that x would have generated wrapper methods for Mutex.Lock, etc.
Interestingly, adding this declaration inside func main (not at package level):
causes the program to succeed. Presumably it causes the compiler to generate the correct type information, which
reflect.StructOf
then finds.The text was updated successfully, but these errors were encountered: