mirror of
https://github.com/golang/go
synced 2024-11-12 00:40:23 -07:00
go/types: implement term lists
This is a straightforward port of CL 339596 to go/types, differing only in the package declaration. Change-Id: If5bf8fd5667bee91b04fdb797702e6045d5fba7b Reviewed-on: https://go-review.googlesource.com/c/go/+/342330 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
3d679c6554
commit
94002f6fca
167
src/go/types/termlist.go
Normal file
167
src/go/types/termlist.go
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
// 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 types
|
||||||
|
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
// A termlist represents the type set represented by the union
|
||||||
|
// t1 ∪ y2 ∪ ... tn of the type sets of the terms t1 to tn.
|
||||||
|
// A termlist is in normal form if all terms are disjoint.
|
||||||
|
// termlist operations don't require the operands to be in
|
||||||
|
// normal form.
|
||||||
|
type termlist []*term
|
||||||
|
|
||||||
|
// topTermList represents the set of all types.
|
||||||
|
// It is in normal form.
|
||||||
|
var topTermlist = termlist{new(term)}
|
||||||
|
|
||||||
|
// String prints the termlist exactly (without normalization).
|
||||||
|
func (xl termlist) String() string {
|
||||||
|
if len(xl) == 0 {
|
||||||
|
return "∅"
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for i, x := range xl {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(" ∪ ")
|
||||||
|
}
|
||||||
|
buf.WriteString(x.String())
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// isEmpty reports whether the termlist xl represents the empty set of types.
|
||||||
|
func (xl termlist) isEmpty() bool {
|
||||||
|
// If there's a non-nil term, the entire list is not empty.
|
||||||
|
// If the termlist is in normal form, this requires at most
|
||||||
|
// one iteration.
|
||||||
|
for _, x := range xl {
|
||||||
|
if x != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTop reports whether the termlist xl represents the set of all types.
|
||||||
|
func (xl termlist) isTop() bool {
|
||||||
|
// If there's a ⊤ (top) term, the entire list is ⊤ (top).
|
||||||
|
// If the termlist is in normal form, this requires at most
|
||||||
|
// one iteration.
|
||||||
|
for _, x := range xl {
|
||||||
|
if x != nil && x.typ == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// norm returns the normal form of xl.
|
||||||
|
func (xl termlist) norm() termlist {
|
||||||
|
// Quadratic algorithm, but good enough for now.
|
||||||
|
// TODO(gri) fix asymptotic performance
|
||||||
|
used := make([]bool, len(xl))
|
||||||
|
var rl termlist
|
||||||
|
for i, xi := range xl {
|
||||||
|
if xi == nil || used[i] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for j := i + 1; j < len(xl); j++ {
|
||||||
|
xj := xl[j]
|
||||||
|
if xj == nil || used[j] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if u1, u2 := xi.union(xj); u2 == nil {
|
||||||
|
// If we encounter a ⊤ (top) term, the entire
|
||||||
|
// list is ⊤ (top). Exit early.
|
||||||
|
// (Note that this is not just an optimization;
|
||||||
|
// if we continue, we may end up with a ⊤ term
|
||||||
|
// and other terms and the result would not be
|
||||||
|
// in normal form.)
|
||||||
|
if u1.typ == nil {
|
||||||
|
return topTermlist
|
||||||
|
}
|
||||||
|
xi = u1
|
||||||
|
used[j] = true // xj is now unioned into xi - ignore it in future iterations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rl = append(rl, xi)
|
||||||
|
}
|
||||||
|
return rl
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the type set represented by xl is specified by a single (non-⊤) term,
|
||||||
|
// structuralType returns that type. Otherwise it returns nil.
|
||||||
|
func (xl termlist) structuralType() Type {
|
||||||
|
if nl := xl.norm(); len(nl) == 1 {
|
||||||
|
return nl[0].typ // if nl.isTop() then typ is nil, which is ok
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// union returns the union xl ∪ yl.
|
||||||
|
func (xl termlist) union(yl termlist) termlist {
|
||||||
|
return append(xl, yl...).norm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// intersect returns the intersection xl ∩ yl.
|
||||||
|
func (xl termlist) intersect(yl termlist) termlist {
|
||||||
|
if xl.isEmpty() || yl.isEmpty() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quadratic algorithm, but good enough for now.
|
||||||
|
// TODO(gri) fix asymptotic performance
|
||||||
|
var rl termlist
|
||||||
|
for _, x := range xl {
|
||||||
|
for _, y := range yl {
|
||||||
|
if r := x.intersect(y); r != nil {
|
||||||
|
rl = append(rl, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rl.norm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// equal reports whether xl and yl represent the same type set.
|
||||||
|
func (xl termlist) equal(yl termlist) bool {
|
||||||
|
// TODO(gri) this should be more efficient
|
||||||
|
return xl.subsetOf(yl) && yl.subsetOf(xl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// includes reports whether t ∈ xl.
|
||||||
|
func (xl termlist) includes(t Type) bool {
|
||||||
|
for _, x := range xl {
|
||||||
|
if x.includes(t) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// supersetOf reports whether y ⊆ xl.
|
||||||
|
func (xl termlist) supersetOf(y *term) bool {
|
||||||
|
for _, x := range xl {
|
||||||
|
if y.subsetOf(x) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// subsetOf reports whether xl ⊆ yl.
|
||||||
|
func (xl termlist) subsetOf(yl termlist) bool {
|
||||||
|
if yl.isEmpty() {
|
||||||
|
return xl.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
// each term x of xl must be a subset of yl
|
||||||
|
for _, x := range xl {
|
||||||
|
if !yl.supersetOf(x) {
|
||||||
|
return false // x is not a subset yl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
278
src/go/types/termlist_test.go
Normal file
278
src/go/types/termlist_test.go
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
// 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 types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// maketl makes a term list from a string of the term list.
|
||||||
|
func maketl(s string) termlist {
|
||||||
|
s = strings.Replace(s, " ", "", -1)
|
||||||
|
names := strings.Split(s, "∪")
|
||||||
|
r := make(termlist, len(names))
|
||||||
|
for i, n := range names {
|
||||||
|
r[i] = testTerm(n)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTermlistTop(t *testing.T) {
|
||||||
|
if !topTermlist.isTop() {
|
||||||
|
t.Errorf("topTermlist is not top")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTermlistString(t *testing.T) {
|
||||||
|
for _, want := range []string{
|
||||||
|
"∅",
|
||||||
|
"⊤",
|
||||||
|
"int",
|
||||||
|
"~int",
|
||||||
|
"∅ ∪ ∅",
|
||||||
|
"⊤ ∪ ⊤",
|
||||||
|
"∅ ∪ ⊤ ∪ int",
|
||||||
|
} {
|
||||||
|
if got := maketl(want).String(); got != want {
|
||||||
|
t.Errorf("(%v).String() == %v", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTermlistIsEmpty(t *testing.T) {
|
||||||
|
for test, want := range map[string]bool{
|
||||||
|
"∅": true,
|
||||||
|
"∅ ∪ ∅": true,
|
||||||
|
"∅ ∪ ∅ ∪ ⊤": false,
|
||||||
|
"⊤": false,
|
||||||
|
"⊤ ∪ int": false,
|
||||||
|
} {
|
||||||
|
xl := maketl(test)
|
||||||
|
got := xl.isEmpty()
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("(%v).isEmpty() == %v; want %v", test, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTermlistIsTop(t *testing.T) {
|
||||||
|
for test, want := range map[string]bool{
|
||||||
|
"∅": false,
|
||||||
|
"∅ ∪ ∅": false,
|
||||||
|
"int ∪ ~string": false,
|
||||||
|
"∅ ∪ ∅ ∪ ⊤": true,
|
||||||
|
"⊤": true,
|
||||||
|
"⊤ ∪ int": true,
|
||||||
|
} {
|
||||||
|
xl := maketl(test)
|
||||||
|
got := xl.isTop()
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("(%v).isTop() == %v; want %v", test, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTermlistNorm(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
xl, want string
|
||||||
|
}{
|
||||||
|
{"∅", "∅"},
|
||||||
|
{"∅ ∪ ∅", "∅"},
|
||||||
|
{"∅ ∪ int", "int"},
|
||||||
|
{"⊤ ∪ int", "⊤"},
|
||||||
|
{"~int ∪ int", "~int"},
|
||||||
|
{"int ∪ ~string ∪ int", "int ∪ ~string"},
|
||||||
|
{"~int ∪ string ∪ ⊤ ∪ ~string ∪ int", "⊤"},
|
||||||
|
} {
|
||||||
|
xl := maketl(test.xl)
|
||||||
|
got := maketl(test.xl).norm()
|
||||||
|
if got.String() != test.want {
|
||||||
|
t.Errorf("(%v).norm() = %v; want %v", xl, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTermlistStructuralType(t *testing.T) {
|
||||||
|
// helper to deal with nil types
|
||||||
|
tstring := func(typ Type) string {
|
||||||
|
if typ == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
return typ.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
for test, want := range map[string]string{
|
||||||
|
"∅": "nil",
|
||||||
|
"⊤": "nil",
|
||||||
|
"int": "int",
|
||||||
|
"~int": "int",
|
||||||
|
"~int ∪ string": "nil",
|
||||||
|
"∅ ∪ int": "int",
|
||||||
|
"∅ ∪ ~int": "int",
|
||||||
|
"∅ ∪ ~int ∪ string": "nil",
|
||||||
|
} {
|
||||||
|
xl := maketl(test)
|
||||||
|
got := tstring(xl.structuralType())
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("(%v).structuralType() == %v; want %v", test, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTermlistUnion(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
xl, yl, want string
|
||||||
|
}{
|
||||||
|
|
||||||
|
{"∅", "∅", "∅"},
|
||||||
|
{"∅", "⊤", "⊤"},
|
||||||
|
{"∅", "int", "int"},
|
||||||
|
{"⊤", "~int", "⊤"},
|
||||||
|
{"int", "~int", "~int"},
|
||||||
|
{"int", "string", "int ∪ string"},
|
||||||
|
{"int ∪ string", "~string", "int ∪ ~string"},
|
||||||
|
{"~int ∪ string", "~string ∪ int", "~int ∪ ~string"},
|
||||||
|
{"~int ∪ string ∪ ∅", "~string ∪ int", "~int ∪ ~string"},
|
||||||
|
{"~int ∪ string ∪ ⊤", "~string ∪ int", "⊤"},
|
||||||
|
} {
|
||||||
|
xl := maketl(test.xl)
|
||||||
|
yl := maketl(test.yl)
|
||||||
|
got := xl.union(yl).String()
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("(%v).union(%v) = %v; want %v", test.xl, test.yl, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTermlistIntersect(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
xl, yl, want string
|
||||||
|
}{
|
||||||
|
|
||||||
|
{"∅", "∅", "∅"},
|
||||||
|
{"∅", "⊤", "∅"},
|
||||||
|
{"∅", "int", "∅"},
|
||||||
|
{"⊤", "~int", "~int"},
|
||||||
|
{"int", "~int", "int"},
|
||||||
|
{"int", "string", "∅"},
|
||||||
|
{"int ∪ string", "~string", "string"},
|
||||||
|
{"~int ∪ string", "~string ∪ int", "int ∪ string"},
|
||||||
|
{"~int ∪ string ∪ ∅", "~string ∪ int", "int ∪ string"},
|
||||||
|
{"~int ∪ string ∪ ⊤", "~string ∪ int", "int ∪ ~string"},
|
||||||
|
} {
|
||||||
|
xl := maketl(test.xl)
|
||||||
|
yl := maketl(test.yl)
|
||||||
|
got := xl.intersect(yl).String()
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("(%v).intersect(%v) = %v; want %v", test.xl, test.yl, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTermlistEqual(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
xl, yl string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{"∅", "∅", true},
|
||||||
|
{"∅", "⊤", false},
|
||||||
|
{"⊤", "⊤", true},
|
||||||
|
{"⊤ ∪ int", "⊤", true},
|
||||||
|
{"⊤ ∪ int", "string ∪ ⊤", true},
|
||||||
|
{"int ∪ ~string", "string ∪ int", false},
|
||||||
|
{"int ∪ ~string ∪ ∅", "string ∪ int ∪ ~string", true},
|
||||||
|
} {
|
||||||
|
xl := maketl(test.xl)
|
||||||
|
yl := maketl(test.yl)
|
||||||
|
got := xl.equal(yl)
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("(%v).equal(%v) = %v; want %v", test.xl, test.yl, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTermlistIncludes(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
xl, typ string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{"∅", "int", false},
|
||||||
|
{"⊤", "int", true},
|
||||||
|
{"~int", "int", true},
|
||||||
|
{"int", "string", false},
|
||||||
|
{"~int", "string", false},
|
||||||
|
{"int ∪ string", "string", true},
|
||||||
|
{"~int ∪ string", "int", true},
|
||||||
|
{"~int ∪ string ∪ ∅", "string", true},
|
||||||
|
{"~string ∪ ∅ ∪ ⊤", "int", true},
|
||||||
|
} {
|
||||||
|
xl := maketl(test.xl)
|
||||||
|
yl := testTerm(test.typ).typ
|
||||||
|
got := xl.includes(yl)
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("(%v).includes(%v) = %v; want %v", test.xl, yl, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTermlistSupersetOf(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
xl, typ string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{"∅", "∅", true},
|
||||||
|
{"∅", "⊤", false},
|
||||||
|
{"∅", "int", false},
|
||||||
|
{"⊤", "∅", true},
|
||||||
|
{"⊤", "⊤", true},
|
||||||
|
{"⊤", "int", true},
|
||||||
|
{"⊤", "~int", true},
|
||||||
|
{"~int", "int", true},
|
||||||
|
{"~int", "~int", true},
|
||||||
|
{"int", "~int", false},
|
||||||
|
{"int", "string", false},
|
||||||
|
{"~int", "string", false},
|
||||||
|
{"int ∪ string", "string", true},
|
||||||
|
{"int ∪ string", "~string", false},
|
||||||
|
{"~int ∪ string", "int", true},
|
||||||
|
{"~int ∪ string ∪ ∅", "string", true},
|
||||||
|
{"~string ∪ ∅ ∪ ⊤", "int", true},
|
||||||
|
} {
|
||||||
|
xl := maketl(test.xl)
|
||||||
|
y := testTerm(test.typ)
|
||||||
|
got := xl.supersetOf(y)
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("(%v).supersetOf(%v) = %v; want %v", test.xl, y, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTermlistSubsetOf(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
xl, yl string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{"∅", "∅", true},
|
||||||
|
{"∅", "⊤", true},
|
||||||
|
{"⊤", "∅", false},
|
||||||
|
{"⊤", "⊤", true},
|
||||||
|
{"int", "int ∪ string", true},
|
||||||
|
{"~int", "int ∪ string", false},
|
||||||
|
{"~int", "string ∪ string ∪ int ∪ ~int", true},
|
||||||
|
{"int ∪ string", "string", false},
|
||||||
|
{"int ∪ string", "string ∪ int", true},
|
||||||
|
{"int ∪ ~string", "string ∪ int", false},
|
||||||
|
{"int ∪ ~string", "string ∪ int ∪ ⊤", true},
|
||||||
|
{"int ∪ ~string", "string ∪ int ∪ ∅ ∪ string", false},
|
||||||
|
} {
|
||||||
|
xl := maketl(test.xl)
|
||||||
|
yl := maketl(test.yl)
|
||||||
|
got := xl.subsetOf(yl)
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("(%v).subsetOf(%v) = %v; want %v", test.xl, test.yl, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user