1
0
mirror of https://github.com/golang/go synced 2024-11-26 15:46:54 -07:00

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>
This commit is contained in:
Dan Scales 2021-03-15 10:27:06 -07:00
parent e31e84010e
commit 832a01aad4
3 changed files with 242 additions and 1 deletions

View File

@ -181,11 +181,20 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
case *types2.Interface:
embeddeds := make([]*types.Field, typ.NumEmbeddeds())
j := 0
for i := range embeddeds {
// TODO(mdempsky): Get embedding position.
e := typ.EmbeddedType(i)
embeddeds[i] = types.NewField(src.NoXPos, nil, g.typ1(e))
if t := types2.AsInterface(e); t != nil && t.IsComparable() {
// Ignore predefined type 'comparable', since it
// doesn't resolve and it doesn't have any
// relevant methods.
continue
}
embeddeds[j] = types.NewField(src.NoXPos, nil, g.typ1(e))
j++
}
embeddeds = embeddeds[:j]
methods := make([]*types.Field, typ.NumExplicitMethods())
for i := range methods {

View File

@ -971,3 +971,4 @@ func asTypeParam(t Type) *TypeParam {
func AsPointer(t Type) *Pointer { return asPointer(t) }
func AsNamed(t Type) *Named { return asNamed(t) }
func AsSignature(t Type) *Signature { return asSignature(t) }
func AsInterface(t Type) *Interface { return asInterface(t) }

231
test/typeparam/graph.go Normal file
View File

@ -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()
}