Skip to content

Better representation for Go's defer #87

Description

@tmr232

Currently there is no special treatment for defer.
This needs to be improved, as defer is a control-flow structure, and a rather complex one at that.

Activity

  1. tmr232 commented on Feb 6, 2025

    @tmr232
    OwnerAuthor

    My first idea was to add deferred nodes when they need to be, and have a dotted or dashed arrow leading to them from the original defer line.
    Sadly, this becomes an unreadable mess.

    View and edit

    digraph "" {
        node [shape=box, color="#000000" label=""];
        edge [headport=n tailport=s]
        bgcolor="#ffffff"
        node0 [style="filled"; label=""; id="node0"; shape="invhouse"; class="entry"; fillcolor="#48AB30"; height=0.5];
        node1 [style="filled"; label=""; id="node1"; shape="box"; class="default"; fillcolor="#d3d3d3"; height=0.3];
        node2 [style="filled"; label=""; id="node2"; shape="box"; class="default"; fillcolor="#d3d3d3"; height=0.3];
        node4 [style="filled"; label=""; id="node4"; shape="house"; class="exit"; fillcolor="#AB3030"; height=0.5];
        node3 [style="filled"; label=""; id="node3"; shape="box"; class="default"; fillcolor="#d3d3d3"; height=0.3];
        node5 [style="filled"; label=""; id="node5"; shape="box"; class="default"; fillcolor="#d3d3d3"; height=0.3];
        node6 [style="filled"; label=""; id="node6"; shape="box"; class="default"; fillcolor="#d3d3d3"; height=0.3];
        node7 [style="filled"; label=""; id="node7"; shape="box"; class="default"; fillcolor="#d3d3d3"; height=0.3];
        node9 [style="filled"; label=""; id="node9"; shape="house"; class="exit"; fillcolor="#AB3030"; height=0.5];
        node8 [style="filled"; label=""; id="node8"; shape="box"; class="default"; fillcolor="#d3d3d3"; height=0.3];
        node10 [style="filled"; label=""; id="node10"; shape="box"; class="default"; fillcolor="#d3d3d3"; height=0.3];
        node11 [style="filled"; label=""; id="node11"; shape="house"; class="exit"; fillcolor="#AB3030"; height=0.5];
        node2 -> node4 [penwidth=1; color="#008000"; class="consequence"];
        node2 -> node3 [penwidth=1; color="#ff0000"; class="alternative"];
        node7 -> defer5_a [penwidth=1; color="#008000"; class="consequence"];
        node5 -> defer5_a [tailport=w headport=w style="dashed"]
        defer5_a -> node9 [penwidth=1; color="#0000ff"; class="regular"];
        node7 -> node8 [penwidth=1; color="#ff0000"; class="alternative"];
        node1 -> node2 [penwidth=1; color="#0000ff"; class="regular"];
        node3 -> node5 [penwidth=1; color="#0000ff"; class="regular"];
        node5 -> node6 [penwidth=1; color="#0000ff"; class="regular"];
        node6 -> node7 [penwidth=1; color="#0000ff"; class="regular"];
        node8 -> node10 [penwidth=1; color="#0000ff"; class="regular"];
        node10 -> defer10 [penwidth=1; color="#0000ff"; class="regular"];
        node0 -> node1 [penwidth=1; color="#0000ff"; class="regular"];
        
        
        node5 -> defer5 [tailport=w headport=w style="dashed"]
        node10 -> defer10 [tailport=w headport=w style="dashed"]
        defer5 -> node11  [penwidth=1; color="#0000ff"; class="regular"];
        defer10 -> defer5  [penwidth=1; color="#0000ff"; class="regular"];
    }

    Image

  2. tmr232 commented on Feb 6, 2025

    @tmr232
    OwnerAuthor

    Next option is to use something similar to Python's context manager:

    Image

    But I don't know if Go devs think of defer that way, so it feels weird.

    Additionally, it's not always a truthful representation.
    A conditional defer (inside an if) would not lead to a context-like behavior.

    Unlike context-managers and exceptions, defer's scope doesn't match the lexical scope in which it is used.

  3. tmr232 commented on Feb 6, 2025

    @tmr232
    OwnerAuthor

    I also thought of doing a node to the side of the "defer" line, but that is, again, messy.

    We could also make it similar to yield, with it's own node shape.
    But I feel it is still different, as yield affects the local control flow where defer does not.

    Maybe an icon? Or a texture?
    I really don't know what to go for here.

  4. tmr232 commented on May 24, 2025

    @tmr232
    OwnerAuthor

    Another issue with defer is that we don't know the order in which defers will happen.

    Consider the following:

    package main
    
    import (
    	"fmt"
    )
    
    func withDefer(flags ...bool) {
    	for _, flag := range flags {
    		if flag {
    			defer fmt.Println("True")
    		} else {
    			defer fmt.Println("False")
    		}
    	}
    }
    
    func main() {
    	withDefer(true, false)
    	withDefer(false, true)
    }

    What would be a good representation here?

    I am starting to think that the best approach would be to add a new block type for it (like yield).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    GoGo language supportenhancementNew feature or requesthelp wantedExtra attention is needed

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions