aboutsummaryrefslogtreecommitdiff
path: root/test/typeparam/graph.go
diff options
context:
space:
mode:
authorDan Scales <danscales@google.com>2021-03-15 10:27:06 -0700
committerDan Scales <danscales@google.com>2021-03-16 18:42:06 +0000
commit832a01aad409dc0e41eabb53bfdb64d1a41b2c83 (patch)
tree4540978653a50809c849b650db5f681fef998deb /test/typeparam/graph.go
parente31e84010e0989e1df4bd2c55d529497119bb345 (diff)
downloadgo-832a01aad409dc0e41eabb53bfdb64d1a41b2c83.tar.gz
go-832a01aad409dc0e41eabb53bfdb64d1a41b2c83.zip
cmd/compile: deal with comparable embedded in a constraint
Ignore an embedded type in an interface which is the predeclared interface "comparable" (which currently can only be in a type constraint), since the name doesn't resolve and the "comparable" type doesn't have any relevant methods (for the purposes of the compiler). Added new test case graph.go that needs this fix. Change-Id: I2443d2c3dfeb9d0a78aaaaf91a2808ae2759d247 Reviewed-on: https://go-review.googlesource.com/c/go/+/301831 Trust: Dan Scales <danscales@google.com> Trust: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
Diffstat (limited to 'test/typeparam/graph.go')
-rw-r--r--test/typeparam/graph.go231
1 files changed, 231 insertions, 0 deletions
diff --git a/test/typeparam/graph.go b/test/typeparam/graph.go
new file mode 100644
index 0000000000..f2a2630ad0
--- /dev/null
+++ b/test/typeparam/graph.go
@@ -0,0 +1,231 @@
+// run -gcflags=-G=3
+
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "errors"
+ "fmt"
+)
+
+// _Equal reports whether two slices are equal: the same length and all
+// elements equal. All floating point NaNs are considered equal.
+func _SliceEqual[Elem comparable](s1, s2 []Elem) bool {
+ if len(s1) != len(s2) {
+ return false
+ }
+ for i, v1 := range s1 {
+ v2 := s2[i]
+ if v1 != v2 {
+ isNaN := func(f Elem) bool { return f != f }
+ if !isNaN(v1) || !isNaN(v2) {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+// A Graph is a collection of nodes. A node may have an arbitrary number
+// of edges. An edge connects two nodes. Both nodes and edges must be
+// comparable. This is an undirected simple graph.
+type _Graph[_Node _NodeC[_Edge], _Edge _EdgeC[_Node]] struct {
+ nodes []_Node
+}
+
+// _NodeC is the contraints on a node in a graph, given the _Edge type.
+type _NodeC[_Edge any] interface {
+ comparable
+ Edges() []_Edge
+}
+
+// Edgec is the constraints on an edge in a graph, given the _Node type.
+type _EdgeC[_Node any] interface {
+ comparable
+ Nodes() (a, b _Node)
+}
+
+// _New creates a new _Graph from a collection of Nodes.
+func _New[_Node _NodeC[_Edge], _Edge _EdgeC[_Node]](nodes []_Node) *_Graph[_Node, _Edge] {
+ return &_Graph[_Node, _Edge]{nodes: nodes}
+}
+
+// nodePath holds the path to a node during ShortestPath.
+// This should ideally be a type defined inside ShortestPath,
+// but the translator tool doesn't support that.
+type nodePath[_Node _NodeC[_Edge], _Edge _EdgeC[_Node]] struct {
+ node _Node
+ path []_Edge
+}
+
+// ShortestPath returns the shortest path between two nodes,
+// as an ordered list of edges. If there are multiple shortest paths,
+// which one is returned is unpredictable.
+func (g *_Graph[_Node, _Edge]) ShortestPath(from, to _Node) ([]_Edge, error) {
+ visited := make(map[_Node]bool)
+ visited[from] = true
+ workqueue := []nodePath[_Node, _Edge]{nodePath[_Node, _Edge]{from, nil}}
+ for len(workqueue) > 0 {
+ current := workqueue
+ workqueue = nil
+ for _, np := range current {
+ edges := np.node.Edges()
+ for _, edge := range edges {
+ a, b := edge.Nodes()
+ if a == np.node {
+ a = b
+ }
+ if !visited[a] {
+ ve := append([]_Edge(nil), np.path...)
+ ve = append(ve, edge)
+ if a == to {
+ return ve, nil
+ }
+ workqueue = append(workqueue, nodePath[_Node, _Edge]{a, ve})
+ visited[a] = true
+ }
+ }
+ }
+ }
+ return nil, errors.New("no path")
+}
+
+type direction int
+
+const (
+ north direction = iota
+ ne
+ east
+ se
+ south
+ sw
+ west
+ nw
+ up
+ down
+)
+
+func (dir direction) String() string {
+ strs := map[direction]string{
+ north: "north",
+ ne: "ne",
+ east: "east",
+ se: "se",
+ south: "south",
+ sw: "sw",
+ west: "west",
+ nw: "nw",
+ up: "up",
+ down: "down",
+ }
+ if str, ok := strs[dir]; ok {
+ return str
+ }
+ return fmt.Sprintf("direction %d", dir)
+}
+
+type mazeRoom struct {
+ index int
+ exits [10]int
+}
+
+type mazeEdge struct {
+ from, to int
+ dir direction
+}
+
+// Edges returns the exits from the room.
+func (m mazeRoom) Edges() []mazeEdge {
+ var r []mazeEdge
+ for i, exit := range m.exits {
+ if exit != 0 {
+ r = append(r, mazeEdge{
+ from: m.index,
+ to: exit,
+ dir: direction(i),
+ })
+ }
+ }
+ return r
+}
+
+// Nodes returns the rooms connected by an edge.
+//go:noinline
+func (e mazeEdge) Nodes() (mazeRoom, mazeRoom) {
+ m1, ok := zork[e.from]
+ if !ok {
+ panic("bad edge")
+ }
+ m2, ok := zork[e.to]
+ if !ok {
+ panic("bad edge")
+ }
+ return m1, m2
+}
+
+// The first maze in Zork. Room indexes based on original Fortran data file.
+// You are in a maze of twisty little passages, all alike.
+var zork = map[int]mazeRoom{
+ 11: {exits: [10]int{north: 11, south: 12, east: 14}}, // west to Troll Room
+ 12: {exits: [10]int{south: 11, north: 14, east: 13}},
+ 13: {exits: [10]int{west: 12, north: 14, up: 16}},
+ 14: {exits: [10]int{west: 13, north: 11, east: 15}},
+ 15: {exits: [10]int{south: 14}}, // Dead End
+ 16: {exits: [10]int{east: 17, north: 13, sw: 18}}, // skeleton, etc.
+ 17: {exits: [10]int{west: 16}}, // Dead End
+ 18: {exits: [10]int{down: 16, east: 19, west: 18, up: 22}},
+ 19: {exits: [10]int{up: 29, west: 18, ne: 15, east: 20, south: 30}},
+ 20: {exits: [10]int{ne: 19, west: 20, se: 21}},
+ 21: {exits: [10]int{north: 20}}, // Dead End
+ 22: {exits: [10]int{north: 18, east: 24, down: 23, south: 28, west: 26, nw: 22}},
+ 23: {exits: [10]int{east: 22, west: 28, up: 24}},
+ 24: {exits: [10]int{ne: 25, down: 23, nw: 28, sw: 26}},
+ 25: {exits: [10]int{sw: 24}}, // Grating room (up to Clearing)
+ 26: {exits: [10]int{west: 16, sw: 24, east: 28, up: 22, north: 27}},
+ 27: {exits: [10]int{south: 26}}, // Dead End
+ 28: {exits: [10]int{east: 22, down: 26, south: 23, west: 24}},
+ 29: {exits: [10]int{west: 30, nw: 29, ne: 19, south: 19}},
+ 30: {exits: [10]int{west: 29, south: 19}}, // ne to Cyclops Room
+}
+
+func TestShortestPath() {
+ // The Zork maze is not a proper undirected simple graph,
+ // as there are some one way paths (e.g., 19 -> 15),
+ // but for this test that doesn't matter.
+
+ // Set the index field in the map. Simpler than doing it in the
+ // composite literal.
+ for k := range zork {
+ r := zork[k]
+ r.index = k
+ zork[k] = r
+ }
+
+ var nodes []mazeRoom
+ for idx, room := range zork {
+ mridx := room
+ mridx.index = idx
+ nodes = append(nodes, mridx)
+ }
+ g := _New[mazeRoom, mazeEdge](nodes)
+ path, err := g.ShortestPath(zork[11], zork[30])
+ if err != nil {
+ panic(fmt.Sprintf("%v", err))
+ }
+ var steps []direction
+ for _, edge := range path {
+ steps = append(steps, edge.dir)
+ }
+ want := []direction{east, west, up, sw, east, south}
+ if !_SliceEqual(steps, want) {
+ panic(fmt.Sprintf("ShortestPath returned %v, want %v", steps, want))
+ }
+}
+
+
+func main() {
+ TestShortestPath()
+}