Open gopherbot opened 13 years ago
I think this underlines a more serious problem which is that code like:- type Foo interface { Bar() interface { Foo } } Compiles without error which seems to me to be an invalid recursive type (unless I'm missing something here). I am happy to submit a new issue for this (with reproducing code - you get nonsensical syntax errors regarding the < of '<...>' when trying to import a package with such a type in, for example). src/cmd/gc/fmt.c:1453 has a mechanism for detecting + dealing with overly deep recursive types when dumping output, i.e.:- if(t->trecur > 4) return fmtstrcpy(fp, "<...>"); However, a small increase in the number of functions within an interface which exhibits this recursive behaviour seems to result in exponentially increasing size of the output type (as inserted at the start of the .6/.8/.? file). This also varies with the limit put on t->trecur, e.g.:- > 0 -> https://gist.github.com/1552355, 69 lines > 1 -> https://gist.github.com/1552361, 1089 lines > 2 -> https://gist.github.com/1552363 20,997 lines I have submitted a trivial patch which puts a limit on the nfmt field of fp to a somewhat arbitrary limit, which fixes the issue with the compiler going down the garden path, but obviously not whether it identifies this situation as invalid recursion:- http://golang.org/cl/5504108/
The underlying problem is that embedded interfaces are substituted by their methods early on, and then the original embedded field is lost. I started by making tointerface() retain the originals, and mark them and their imported methods as ->embedded. then typefmt can print skip the embedded methods and print the interface they came from (possibly only for recursive types). This involves skipping over the now-retained TINTER fields in some places and making go.y accept them too. Since it's nearly weekend and there's no rsc to bounce this off off, i thought i'd write it up here. work in progress in http://golang.org/cl/5523047
Pending in http://golang.org/cl/5523047/, but that still needs a fix to exp/types/gcimporter.
The original patch for this had quite many problems: I have given a try in https://golang.org/cl/7232050/ but it still doesn't work. The suggested approach raises certain problems: * interfaces defined in export data may forward declare types. Types are necessary: to resolve embedded interfaces, to typecheck signatures, to check equality of interface types. The current parser assumes a particular order in export data which is no longer satisfied after patching. * things must be correctly exported to make sure the binary does not end up with two type definitions and names for identical interface types: a program may compile fine but fail at runtime because conversion to interface is lost in method tables
Here's another good test case: package main type ( A interface { a() interface { ABC1 } } B interface { b() interface { ABC2 } } C interface { c() interface { ABC3 } } AB interface { A B } BC interface { B C } ABC1 interface { A B C } ABC2 interface { AB C } ABC3 interface { A BC } ) var ( x1 ABC1 x2 ABC2 x3 ABC3 ) func main() { x1 = x2 x2 = x3 x2 = x3 } This causes the compiler to go into an infinite (probably) recursion (memory is consumed until machine becomes unresponsive). All types ABC1, ABC2, ABC3 are identical interface types, but they are constructed in different ways in the source. Here's a black-box analysis. There are several problems for a front-end to solve: 1) It must correctly create the internal cyclic type representation for these interface types. The representation must be based on the methods, not the actual source code (since there are different ways to get to the same method set). That representation is cyclic in this case, and the recursion is not represented by ending in a named type (since the method result types here are unnamed); it's a true cycle in the type representation. 2) When type-checking the assignments, the compiler must verify that the lhs and rhs have identical types. That is, in this case it must verify that the cyclic graph on the lhs matches the cyclic graph on the rhs. Because the cycles are not created with named types, the graph-comparison algorithm must use some marking scheme (visited map) to detect cycles. 3) The export data structure for such types (currently we can only have this for interface types) must be able to represent such recursion. Specifically, it either reproduces the original source definition (w/ embedding information intact) so it can use the existing type names to express the recursion; or it needs some other mechanism (linearization of arbitrary cyclic graph comes to mind - easy, but not done at the moment).
See also issue #6589.
CL https://golang.org/cl/147220043 mentions this issue.
CL https://golang.org/cl/13937 mentions this issue.
There are (at least) two endless recursive function calls involved here:
Both of these use gc's fmt.go facilities to print and then run into endless recursion.
The new binary export format (https://go-review.googlesource.com/13937) fixes the 2nd problem since it can handle arbitrary cycles in types: If we immediately return from dumptypestructs (i.e., just don't dump reflection info for the test), exporting works fine with -newexport. If we use the old export format, we get into an endless recursion while exporting.
We may need to use a similar mechanism to export reflection info as we use for the new binary export.
CL https://golang.org/cl/31859 mentions this issue.
by lcampbell@ironclad.mobi: