go/types, types2: move exported predicates into separate file

This allows those functions to be generated for go/types.
Also, change the generator's renameIdent mechanism so that
it can rename multiple identifiers in one pass through the
AST instead of requiring multiple passes.

No type-checker functionality changes.

Change-Id: Ic78d899c6004b6a0692a95902fdc13f8ffb47824
Reviewed-on: https://go-review.googlesource.com/c/go/+/542757
Run-TryBot: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
This commit is contained in:
Robert Griesemer 2023-11-13 16:46:47 -08:00 committed by Gopher Robot
parent a95c5d37f2
commit e62bf150cc
5 changed files with 199 additions and 173 deletions

View File

@ -460,80 +460,3 @@ func (conf *Config) Check(path string, files []*syntax.File, info *Info) (*Packa
pkg := NewPackage(path, "")
return pkg, NewChecker(conf, pkg, info).Files(files)
}
// AssertableTo reports whether a value of type V can be asserted to have type T.
//
// The behavior of AssertableTo is unspecified in three cases:
// - if T is Typ[Invalid]
// - if V is a generalized interface; i.e., an interface that may only be used
// as a type constraint in Go code
// - if T is an uninstantiated generic type
func AssertableTo(V *Interface, T Type) bool {
// Checker.newAssertableTo suppresses errors for invalid types, so we need special
// handling here.
if !isValid(T.Underlying()) {
return false
}
return (*Checker)(nil).newAssertableTo(nopos, V, T, nil)
}
// AssignableTo reports whether a value of type V is assignable to a variable
// of type T.
//
// The behavior of AssignableTo is unspecified if V or T is Typ[Invalid] or an
// uninstantiated generic type.
func AssignableTo(V, T Type) bool {
x := operand{mode: value, typ: V}
ok, _ := x.assignableTo(nil, T, nil) // check not needed for non-constant x
return ok
}
// ConvertibleTo reports whether a value of type V is convertible to a value of
// type T.
//
// The behavior of ConvertibleTo is unspecified if V or T is Typ[Invalid] or an
// uninstantiated generic type.
func ConvertibleTo(V, T Type) bool {
x := operand{mode: value, typ: V}
return x.convertibleTo(nil, T, nil) // check not needed for non-constant x
}
// Implements reports whether type V implements interface T.
//
// The behavior of Implements is unspecified if V is Typ[Invalid] or an uninstantiated
// generic type.
func Implements(V Type, T *Interface) bool {
if T.Empty() {
// All types (even Typ[Invalid]) implement the empty interface.
return true
}
// Checker.implements suppresses errors for invalid types, so we need special
// handling here.
if !isValid(V.Underlying()) {
return false
}
return (*Checker)(nil).implements(nopos, V, T, false, nil)
}
// Satisfies reports whether type V satisfies the constraint T.
//
// The behavior of Satisfies is unspecified if V is Typ[Invalid] or an uninstantiated
// generic type.
func Satisfies(V Type, T *Interface) bool {
return (*Checker)(nil).implements(nopos, V, T, true, nil)
}
// Identical reports whether x and y are identical types.
// Receivers of Signature types are ignored.
func Identical(x, y Type) bool {
var c comparer
return c.identical(x, y, nil)
}
// IdenticalIgnoreTags reports whether x and y are identical types if tags are ignored.
// Receivers of Signature types are ignored.
func IdenticalIgnoreTags(x, y Type) bool {
var c comparer
c.ignoreTags = true
return c.identical(x, y, nil)
}

View File

@ -0,0 +1,84 @@
// Copyright 2023 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.
// This file implements exported type predicates.
package types2
// AssertableTo reports whether a value of type V can be asserted to have type T.
//
// The behavior of AssertableTo is unspecified in three cases:
// - if T is Typ[Invalid]
// - if V is a generalized interface; i.e., an interface that may only be used
// as a type constraint in Go code
// - if T is an uninstantiated generic type
func AssertableTo(V *Interface, T Type) bool {
// Checker.newAssertableTo suppresses errors for invalid types, so we need special
// handling here.
if !isValid(T.Underlying()) {
return false
}
return (*Checker)(nil).newAssertableTo(nopos, V, T, nil)
}
// AssignableTo reports whether a value of type V is assignable to a variable
// of type T.
//
// The behavior of AssignableTo is unspecified if V or T is Typ[Invalid] or an
// uninstantiated generic type.
func AssignableTo(V, T Type) bool {
x := operand{mode: value, typ: V}
ok, _ := x.assignableTo(nil, T, nil) // check not needed for non-constant x
return ok
}
// ConvertibleTo reports whether a value of type V is convertible to a value of
// type T.
//
// The behavior of ConvertibleTo is unspecified if V or T is Typ[Invalid] or an
// uninstantiated generic type.
func ConvertibleTo(V, T Type) bool {
x := operand{mode: value, typ: V}
return x.convertibleTo(nil, T, nil) // check not needed for non-constant x
}
// Implements reports whether type V implements interface T.
//
// The behavior of Implements is unspecified if V is Typ[Invalid] or an uninstantiated
// generic type.
func Implements(V Type, T *Interface) bool {
if T.Empty() {
// All types (even Typ[Invalid]) implement the empty interface.
return true
}
// Checker.implements suppresses errors for invalid types, so we need special
// handling here.
if !isValid(V.Underlying()) {
return false
}
return (*Checker)(nil).implements(nopos, V, T, false, nil)
}
// Satisfies reports whether type V satisfies the constraint T.
//
// The behavior of Satisfies is unspecified if V is Typ[Invalid] or an uninstantiated
// generic type.
func Satisfies(V Type, T *Interface) bool {
return (*Checker)(nil).implements(nopos, V, T, true, nil)
}
// Identical reports whether x and y are identical types.
// Receivers of [Signature] types are ignored.
func Identical(x, y Type) bool {
var c comparer
return c.identical(x, y, nil)
}
// IdenticalIgnoreTags reports whether x and y are identical types if tags are ignored.
// Receivers of [Signature] types are ignored.
func IdenticalIgnoreTags(x, y Type) bool {
var c comparer
c.ignoreTags = true
return c.identical(x, y, nil)
}

View File

@ -448,80 +448,3 @@ func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, i
pkg := NewPackage(path, "")
return pkg, NewChecker(conf, fset, pkg, info).Files(files)
}
// AssertableTo reports whether a value of type V can be asserted to have type T.
//
// The behavior of AssertableTo is unspecified in three cases:
// - if T is Typ[Invalid]
// - if V is a generalized interface; i.e., an interface that may only be used
// as a type constraint in Go code
// - if T is an uninstantiated generic type
func AssertableTo(V *Interface, T Type) bool {
// Checker.newAssertableTo suppresses errors for invalid types, so we need special
// handling here.
if !isValid(T.Underlying()) {
return false
}
return (*Checker)(nil).newAssertableTo(nopos, V, T, nil)
}
// AssignableTo reports whether a value of type V is assignable to a variable
// of type T.
//
// The behavior of AssignableTo is unspecified if V or T is Typ[Invalid] or an
// uninstantiated generic type.
func AssignableTo(V, T Type) bool {
x := operand{mode: value, typ: V}
ok, _ := x.assignableTo(nil, T, nil) // check not needed for non-constant x
return ok
}
// ConvertibleTo reports whether a value of type V is convertible to a value of
// type T.
//
// The behavior of ConvertibleTo is unspecified if V or T is Typ[Invalid] or an
// uninstantiated generic type.
func ConvertibleTo(V, T Type) bool {
x := operand{mode: value, typ: V}
return x.convertibleTo(nil, T, nil) // check not needed for non-constant x
}
// Implements reports whether type V implements interface T.
//
// The behavior of Implements is unspecified if V is Typ[Invalid] or an uninstantiated
// generic type.
func Implements(V Type, T *Interface) bool {
if T.Empty() {
// All types (even Typ[Invalid]) implement the empty interface.
return true
}
// Checker.implements suppresses errors for invalid types, so we need special
// handling here.
if !isValid(V.Underlying()) {
return false
}
return (*Checker)(nil).implements(0, V, T, false, nil)
}
// Satisfies reports whether type V satisfies the constraint T.
//
// The behavior of Satisfies is unspecified if V is Typ[Invalid] or an uninstantiated
// generic type.
func Satisfies(V Type, T *Interface) bool {
return (*Checker)(nil).implements(0, V, T, true, nil)
}
// Identical reports whether x and y are identical types.
// Receivers of [Signature] types are ignored.
func Identical(x, y Type) bool {
var c comparer
return c.identical(x, y, nil)
}
// IdenticalIgnoreTags reports whether x and y are identical types if tags are ignored.
// Receivers of [Signature] types are ignored.
func IdenticalIgnoreTags(x, y Type) bool {
var c comparer
c.ignoreTags = true
return c.identical(x, y, nil)
}

View File

@ -0,0 +1,86 @@
// Code generated by "go test -run=Generate -write=all"; DO NOT EDIT.
// Copyright 2023 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.
// This file implements exported type predicates.
package types
// AssertableTo reports whether a value of type V can be asserted to have type T.
//
// The behavior of AssertableTo is unspecified in three cases:
// - if T is Typ[Invalid]
// - if V is a generalized interface; i.e., an interface that may only be used
// as a type constraint in Go code
// - if T is an uninstantiated generic type
func AssertableTo(V *Interface, T Type) bool {
// Checker.newAssertableTo suppresses errors for invalid types, so we need special
// handling here.
if !isValid(T.Underlying()) {
return false
}
return (*Checker)(nil).newAssertableTo(nopos, V, T, nil)
}
// AssignableTo reports whether a value of type V is assignable to a variable
// of type T.
//
// The behavior of AssignableTo is unspecified if V or T is Typ[Invalid] or an
// uninstantiated generic type.
func AssignableTo(V, T Type) bool {
x := operand{mode: value, typ: V}
ok, _ := x.assignableTo(nil, T, nil) // check not needed for non-constant x
return ok
}
// ConvertibleTo reports whether a value of type V is convertible to a value of
// type T.
//
// The behavior of ConvertibleTo is unspecified if V or T is Typ[Invalid] or an
// uninstantiated generic type.
func ConvertibleTo(V, T Type) bool {
x := operand{mode: value, typ: V}
return x.convertibleTo(nil, T, nil) // check not needed for non-constant x
}
// Implements reports whether type V implements interface T.
//
// The behavior of Implements is unspecified if V is Typ[Invalid] or an uninstantiated
// generic type.
func Implements(V Type, T *Interface) bool {
if T.Empty() {
// All types (even Typ[Invalid]) implement the empty interface.
return true
}
// Checker.implements suppresses errors for invalid types, so we need special
// handling here.
if !isValid(V.Underlying()) {
return false
}
return (*Checker)(nil).implements(nopos, V, T, false, nil)
}
// Satisfies reports whether type V satisfies the constraint T.
//
// The behavior of Satisfies is unspecified if V is Typ[Invalid] or an uninstantiated
// generic type.
func Satisfies(V Type, T *Interface) bool {
return (*Checker)(nil).implements(nopos, V, T, true, nil)
}
// Identical reports whether x and y are identical types.
// Receivers of [Signature] types are ignored.
func Identical(x, y Type) bool {
var c comparer
return c.identical(x, y, nil)
}
// IdenticalIgnoreTags reports whether x and y are identical types if tags are ignored.
// Receivers of [Signature] types are ignored.
func IdenticalIgnoreTags(x, y Type) bool {
var c comparer
c.ignoreTags = true
return c.identical(x, y, nil)
}

View File

@ -95,16 +95,17 @@ func generate(t *testing.T, filename string, write bool) {
type action func(in *ast.File)
var filemap = map[string]action{
"alias.go": nil,
"array.go": nil,
"basic.go": nil,
"chan.go": nil,
"const.go": func(f *ast.File) { fixTokenPos(f) },
"context.go": nil,
"context_test.go": nil,
"gccgosizes.go": nil,
"gcsizes.go": func(f *ast.File) { renameIdent(f, "IsSyncAtomicAlign64", "_IsSyncAtomicAlign64") },
"hilbert_test.go": func(f *ast.File) { renameImportPath(f, `"cmd/compile/internal/types2"`, `"go/types"`) },
"alias.go": nil,
"array.go": nil,
"api_predicates.go": nil,
"basic.go": nil,
"chan.go": nil,
"const.go": func(f *ast.File) { fixTokenPos(f) },
"context.go": nil,
"context_test.go": nil,
"gccgosizes.go": nil,
"gcsizes.go": func(f *ast.File) { renameIdents(f, "IsSyncAtomicAlign64->_IsSyncAtomicAlign64") },
"hilbert_test.go": func(f *ast.File) { renameImportPath(f, `"cmd/compile/internal/types2"`, `"go/types"`) },
"infer.go": func(f *ast.File) {
fixTokenPos(f)
fixInferSig(f)
@ -116,7 +117,7 @@ var filemap = map[string]action{
"main_test.go": nil,
"map.go": nil,
"named.go": func(f *ast.File) { fixTokenPos(f); fixTraceSel(f) },
"object.go": func(f *ast.File) { fixTokenPos(f); renameIdent(f, "NewTypeNameLazy", "_NewTypeNameLazy") },
"object.go": func(f *ast.File) { fixTokenPos(f); renameIdents(f, "NewTypeNameLazy->_NewTypeNameLazy") },
"object_test.go": func(f *ast.File) { renameImportPath(f, `"cmd/compile/internal/types2"`, `"go/types"`) },
"objset.go": nil,
"package.go": nil,
@ -124,11 +125,10 @@ var filemap = map[string]action{
"predicates.go": nil,
"scope.go": func(f *ast.File) {
fixTokenPos(f)
renameIdent(f, "Squash", "squash")
renameIdent(f, "InsertLazy", "_InsertLazy")
renameIdents(f, "Squash->squash", "InsertLazy->_InsertLazy")
},
"selection.go": nil,
"sizes.go": func(f *ast.File) { renameIdent(f, "IsSyncAtomicAlign64", "_IsSyncAtomicAlign64") },
"sizes.go": func(f *ast.File) { renameIdents(f, "IsSyncAtomicAlign64->_IsSyncAtomicAlign64") },
"slice.go": nil,
"subst.go": func(f *ast.File) { fixTokenPos(f); fixTraceSel(f) },
"termlist.go": nil,
@ -148,14 +148,24 @@ var filemap = map[string]action{
// TODO(gri) We should be able to make these rewriters more configurable/composable.
// For now this is a good starting point.
// renameIdent renames an identifier.
// Note: This doesn't change the use of the identifier in comments.
func renameIdent(f *ast.File, from, to string) {
// renameIdent renames identifiers: each renames entry is of the form from->to.
// Note: This doesn't change the use of the identifiers in comments.
func renameIdents(f *ast.File, renames ...string) {
var list [][]string
for _, r := range renames {
s := strings.Split(r, "->")
if len(s) != 2 {
panic("invalid rename entry: " + r)
}
list = append(list, s)
}
ast.Inspect(f, func(n ast.Node) bool {
switch n := n.(type) {
case *ast.Ident:
if n.Name == from {
n.Name = to
for _, r := range list {
if n.Name == r[0] {
n.Name = r[1]
}
}
return false
}