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

cmd/compile: recursive anonymous interface defs compile but fail to run #10222

Closed
ghost opened this issue Mar 23, 2015 · 6 comments
Closed

cmd/compile: recursive anonymous interface defs compile but fail to run #10222

ghost opened this issue Mar 23, 2015 · 6 comments
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Milestone

Comments

@ghost
Copy link

ghost commented Mar 23, 2015

Should this program work?

package main

type T interface {
    m() interface {T}
}

type foo struct {
}

func (f *foo) m() interface {T} {
  return &foo{}
}

func main() {
    var t T
    t = &foo{}
    t.m()
}

It seems to compile but panics when run:
http://play.golang.org/p/swxTnFlwQI

By contrast this program works fine:

package main

type T interface {
    m() T
}

type foo struct {
}

func (f *foo) m() T {
    return &foo{}
}

func main() {
    var t T
    t = &foo{}
    t.m()
}

http://play.golang.org/p/Bxnf72xPsn

@griesemer
Copy link
Contributor

Yes, both these programs should compile and run. This appears to be a bug in the gc compiler and/or runtime. I suspect it's the compiler since it has other known issues with recursively defined anonymous interfaces.

For comparison, the first program runs fine when compiled with gccgo.

@bradfitz bradfitz changed the title Recursive anonymous interface defs compile but fail to run cmd/gc: recursive anonymous interface defs compile but fail to run Mar 23, 2015
@bradfitz bradfitz added this to the Go1.5 milestone Mar 23, 2015
@rsc rsc removed the compiler-bug label Apr 10, 2015
@mwhudson
Copy link
Contributor

I had a poke at this, and I think I have an inkling of what's going on. In some sense the type symbol for T should be an infinite string

interface { f() interface { f() interface { f() interface { ...

(with an infinite balancing number of }s).

Now it's clear that if type symbols really could be infinite, the interface string for interface { T } is the same. But in fact of course type symbols cannot be infinite and the compiler truncates at a certain level of nesting, and the levels are different for T and interface { T }. so the type symbols are different (it's something eyeball melting like "interface { main.m() interface { main.m() interface { main.m() interface { main.m() interface { main.m() interface { main.m<...> } } } } } }" vs "interface { main.m() interface { main.m() interface { main.m() interface { main.m() interface { main.m<...> } } } } }"). I'm not entirely sure why the Trecur check in TConv triggers at different depths, but I find that code fairly confusing. It's possible it could be bodged but a real fix probably needs a real computer scientist (skimming wikipedia suggests what we are doing here is trying to compute type equivalence in an equirecursive type system and this paper: http://gallium.inria.fr/~fpottier/publis/gauthier-fpottier-icfp04.pdf)

@griesemer
Copy link
Contributor

Fundamentally, this type is recursive; internally this means that the data structure representing the type has a (pointer) loop. More generally, this can happen with any type data structure containing loops.

Trying to serialize such a data structure in finite form (e.g., print it in text form) requires some mechanism to break the loop.

The straight-forward algorithm to serialize cyclic data structures is to mark (e.g., number) each node, and when a node is encountered that was seen before during serialization, instead of serializing it again, use that number to refer to it.

It may be possible to export nodes in a clever order (after sorting them topologically) and preserve the current textual export format that emulates source code. Cycles would be broken by using identifiers, similar to what we do when we write cyclic type definitions in the first place.

Another alternative is to ditch the current format and use an algorithm that can handle cycles from the outset. x/tools/go/importer implements this for go/types.

@mwhudson
Copy link
Contributor

On 21 April 2015 at 11:53, Robert Griesemer notifications@github.com
wrote:

Fundamentally, this type is recursive; internally this means that the data
structure representing the type has a (pointer) loop. More generally, this
can happen with any type data structure containing loops.

Trying to serialize such a data structure in finite form (e.g., print it
in text form) requires some mechanism to break the loop.

Right.

The straight-forward algorithm to serialize cyclic data structures is to

mark (e.g., number) each node, and when a node is encountered that was seen
before during serialization, instead of serializing it again, use that
number to refer to it.

I did actually try to implement this, but ran into some grotty problem or
other. I was only hacking on the plane though, a more principled attempt
may well work fine.

It may be possible to export nodes in a clever order (after sorting them
topologically) and preserve the current textual export format that emulates
source code. Cycles would be broken by using identifiers, similar to what
we do when we write cyclic type definitions in the first place.

Another alternative is to ditch the current format and use an algorithm
the can handle cycles from the outset. x/tools/go/importer implements this
for go/types.

My (half remembered) impression is that trying to hack a (textually) small
fix is not really the way to go, so that would favour this sort of
approach. I don't have any long haul flights for a while though :-)

@rsc rsc modified the milestones: Unplanned, Go1.5 May 19, 2015
@rsc rsc changed the title cmd/gc: recursive anonymous interface defs compile but fail to run cmd/compile: recursive anonymous interface defs compile but fail to run Jun 8, 2015
@rsc
Copy link
Contributor

rsc commented Sep 26, 2016

This is very low priority and no one is planning to fix it, unless someone provides evidence that it's important for some reason.

The new export data should make this a bit easier, and at least now this is a compile failure as opposed to a death at run time.

@rsc rsc added the NeedsFix The path to resolution is known, but the work has not been done. label Sep 26, 2016
@griesemer
Copy link
Contributor

There's a newer issue that has more information. I'm going to close this one as duplicate of #16369.

@golang golang locked and limited conversation to collaborators Sep 26, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Projects
None yet
Development

No branches or pull requests

5 participants