blob: 66c7be225fb60438666b03ef127ba77f7adc20d9 [file] [log] [blame]
// Copyright 2022 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.
//go:build go1.18
// +build go1.18
package main
import (
"bytes"
"errors"
"fmt"
"go/ast"
"go/parser"
"go/token"
"go/types"
"log"
)
const src = `
//!+input
package p
type Pair[L, R comparable] struct {
left L
right R
}
func (p Pair[L, _]) Left() L {
return p.left
}
func Equal[L, R comparable](x, y Pair[L, R]) bool {
return x.left == y.left && x.right == y.right
}
var X Pair[int, string]
var Y Pair[string, int]
var E = Equal[int, string]
//!-input
`
//!+check
func CheckInstances(fset *token.FileSet, file *ast.File) (*types.Package, error) {
conf := types.Config{}
info := &types.Info{
Instances: make(map[*ast.Ident]types.Instance),
}
pkg, err := conf.Check("p", fset, []*ast.File{file}, info)
for id, inst := range info.Instances {
posn := fset.Position(id.Pos())
fmt.Printf("%s: %s instantiated with %s: %s\n", posn, id.Name, FormatTypeList(inst.TypeArgs), inst.Type)
}
return pkg, err
}
//!-check
/*
//!+checkoutput
hello.go:21:9: Equal instantiated with [int, string]: func(x p.Pair[int, string], y p.Pair[int, string]) bool
hello.go:10:9: Pair instantiated with [L, _]: p.Pair[L, _]
hello.go:14:34: Pair instantiated with [L, R]: p.Pair[L, R]
hello.go:18:7: Pair instantiated with [int, string]: p.Pair[int, string]
hello.go:19:7: Pair instantiated with [string, int]: p.Pair[string, int]
//!-checkoutput
*/
func FormatTypeList(list *types.TypeList) string {
var buf bytes.Buffer
buf.WriteRune('[')
for i := 0; i < list.Len(); i++ {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(list.At(i).String())
}
buf.WriteRune(']')
return buf.String()
}
//!+instantiate
func Instantiate(pkg *types.Package) error {
Pair := pkg.Scope().Lookup("Pair").Type()
X := pkg.Scope().Lookup("X").Type()
Y := pkg.Scope().Lookup("Y").Type()
// X and Y have different types, because their type arguments are different.
Compare("X", "Y", X, Y)
// Create a shared context for the subsequent instantiations.
ctxt := types.NewContext()
// Instantiating with [int, string] yields an instance that is identical (but
// not equal) to X.
Int, String := types.Typ[types.Int], types.Typ[types.String]
inst1, _ := types.Instantiate(ctxt, Pair, []types.Type{Int, String}, true)
Compare("X", "inst1", X, inst1)
// Instantiating again returns the same exact instance, because of the shared
// Context.
inst2, _ := types.Instantiate(ctxt, Pair, []types.Type{Int, String}, true)
Compare("inst1", "inst2", inst1, inst2)
// Instantiating with 'any' is an error, because any is not comparable.
Any := types.Universe.Lookup("any").Type()
_, err := types.Instantiate(ctxt, Pair, []types.Type{Int, Any}, true)
var argErr *types.ArgumentError
if errors.As(err, &argErr) {
fmt.Printf("Argument %d: %v\n", argErr.Index, argErr.Err)
}
return nil
}
func Compare(leftName, rightName string, left, right types.Type) {
fmt.Printf("Identical(%s, %s) : %t\n", leftName, rightName, types.Identical(left, right))
fmt.Printf("%s == %s : %t\n\n", leftName, rightName, left == right)
}
//!-instantiate
/*
//!+instantiateoutput
Identical(p.Pair[int, string], p.Pair[string, int]) : false
p.Pair[int, string] == p.Pair[string, int] : false
Identical(p.Pair[int, string], p.Pair[int, string]) : true
p.Pair[int, string] == p.Pair[int, string] : false
Identical(p.Pair[string, int], p.Pair[int, string]) : false
p.Pair[string, int] == p.Pair[int, string] : false
Identical(p.Pair[int, string], p.Pair[int, string]) : true
p.Pair[int, string] == p.Pair[int, string] : true
Argument 1: any does not implement comparable
//!-instantiateoutput
*/
func main() {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "hello.go", src, 0)
if err != nil {
log.Fatal(err)
}
fmt.Println("=== CheckInstances ===")
pkg, err := CheckInstances(fset, f)
if err != nil {
log.Fatal(err)
}
fmt.Println("=== Instantiate ===")
if err := Instantiate(pkg); err != nil {
log.Fatal(err)
}
}