mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
go.tools: add go/types, ssa, and cmd/vet
They will be deleted from their current homes once this has landed. Changes made to import paths to make the code compile, and to find errchk in the right place in cmd/vet's Makefile. TODO in a later CL: tidy up vet. R=golang-dev, gri CC=golang-dev https://golang.org/cl/9495043
This commit is contained in:
parent
9f31513c92
commit
01f8cd246d
14
cmd/vet/Makefile
Normal file
14
cmd/vet/Makefile
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Copyright 2010 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.
|
||||||
|
|
||||||
|
# Assumes go/types is installed
|
||||||
|
test testshort:
|
||||||
|
go build -tags 'vet_test gotypes'
|
||||||
|
$(GOROOT)/test/errchk ./vet -printfuncs='Warn:1,Warnf:1' test_*.go test_*.s
|
||||||
|
|
||||||
|
test_notypes:
|
||||||
|
go build -tags 'vet_test'
|
||||||
|
# Only those tests that do not depend on types.
|
||||||
|
# Excluded: test_print.go
|
||||||
|
$(GOROOT)/test/errchk ./vet -printfuncs='Warn:1,Warnf:1' test_asm.go test_assign.go test_atomic.go test_buildtag.go test_buildtag_bad.go test_deadcode.go test_method.go test_rangeloop.go test_structtag.go test_taglit.go test_*.s
|
533
cmd/vet/asmdecl.go
Normal file
533
cmd/vet/asmdecl.go
Normal file
@ -0,0 +1,533 @@
|
|||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
// Identify mismatches between assembly files and Go func declarations.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 'kind' is a kind of assembly variable.
|
||||||
|
// The kinds 1, 2, 4, 8 stand for values of that size.
|
||||||
|
type asmKind int
|
||||||
|
|
||||||
|
// These special kinds are not valid sizes.
|
||||||
|
const (
|
||||||
|
asmString asmKind = 100 + iota
|
||||||
|
asmSlice
|
||||||
|
asmInterface
|
||||||
|
asmEmptyInterface
|
||||||
|
)
|
||||||
|
|
||||||
|
// An asmArch describes assembly parameters for an architecture
|
||||||
|
type asmArch struct {
|
||||||
|
name string
|
||||||
|
ptrSize int
|
||||||
|
intSize int
|
||||||
|
bigEndian bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// An asmFunc describes the expected variables for a function on a given architecture.
|
||||||
|
type asmFunc struct {
|
||||||
|
arch *asmArch
|
||||||
|
size int // size of all arguments
|
||||||
|
vars map[string]*asmVar
|
||||||
|
varByOffset map[int]*asmVar
|
||||||
|
}
|
||||||
|
|
||||||
|
// An asmVar describes a single assembly variable.
|
||||||
|
type asmVar struct {
|
||||||
|
name string
|
||||||
|
kind asmKind
|
||||||
|
typ string
|
||||||
|
off int
|
||||||
|
size int
|
||||||
|
inner []*asmVar
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
asmArch386 = asmArch{"386", 4, 4, false}
|
||||||
|
asmArchArm = asmArch{"arm", 4, 4, false}
|
||||||
|
asmArchAmd64 = asmArch{"amd64", 8, 8, false}
|
||||||
|
|
||||||
|
arches = []*asmArch{
|
||||||
|
&asmArch386,
|
||||||
|
&asmArchArm,
|
||||||
|
&asmArchAmd64,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
re = regexp.MustCompile
|
||||||
|
asmPlusBuild = re(`//\s+\+build\s+([^\n]+)`)
|
||||||
|
asmTEXT = re(`\bTEXT\b.*·([^\(]+)\(SB\)(?:\s*,\s*([0-9]+))?(?:\s*,\s*\$([0-9]+)(?:-([0-9]+))?)?`)
|
||||||
|
asmDATA = re(`\b(DATA|GLOBL)\b`)
|
||||||
|
asmNamedFP = re(`([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`)
|
||||||
|
asmUnnamedFP = re(`[^+\-0-9]](([0-9]+)\(FP\))`)
|
||||||
|
asmOpcode = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`)
|
||||||
|
)
|
||||||
|
|
||||||
|
func asmCheck(pkg *Package) {
|
||||||
|
if !vet("asmdecl") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// No work if no assembly files.
|
||||||
|
if !pkg.hasFileWithSuffix(".s") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather declarations. knownFunc[name][arch] is func description.
|
||||||
|
knownFunc := make(map[string]map[string]*asmFunc)
|
||||||
|
|
||||||
|
for _, f := range pkg.files {
|
||||||
|
if f.file != nil {
|
||||||
|
for _, decl := range f.file.Decls {
|
||||||
|
if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body == nil {
|
||||||
|
knownFunc[decl.Name.Name] = f.asmParseDecl(decl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var fn *asmFunc
|
||||||
|
for _, f := range pkg.files {
|
||||||
|
if !strings.HasSuffix(f.name, ".s") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
Println("Checking file", f.name)
|
||||||
|
|
||||||
|
// Determine architecture from file name if possible.
|
||||||
|
var arch string
|
||||||
|
for _, a := range arches {
|
||||||
|
if strings.HasSuffix(f.name, "_"+a.name+".s") {
|
||||||
|
arch = a.name
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.SplitAfter(string(f.content), "\n")
|
||||||
|
for lineno, line := range lines {
|
||||||
|
lineno++
|
||||||
|
|
||||||
|
warnf := func(format string, args ...interface{}) {
|
||||||
|
f.Warnf(token.NoPos, "%s:%d: [%s] %s", f.name, lineno, arch, fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
if arch == "" {
|
||||||
|
// Determine architecture from +build line if possible.
|
||||||
|
if m := asmPlusBuild.FindStringSubmatch(line); m != nil {
|
||||||
|
Fields:
|
||||||
|
for _, fld := range strings.Fields(m[1]) {
|
||||||
|
for _, a := range arches {
|
||||||
|
if a.name == fld {
|
||||||
|
arch = a.name
|
||||||
|
break Fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if m := asmTEXT.FindStringSubmatch(line); m != nil {
|
||||||
|
if arch == "" {
|
||||||
|
f.Warnf(token.NoPos, "%s: cannot determine architecture for assembly file", f.name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fn = knownFunc[m[1]][arch]
|
||||||
|
if fn != nil {
|
||||||
|
size, _ := strconv.Atoi(m[4])
|
||||||
|
if size != fn.size && (m[2] != "7" || size != 0) {
|
||||||
|
warnf("wrong argument size %d; expected $...-%d", size, fn.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else if strings.Contains(line, "TEXT") && strings.Contains(line, "SB") {
|
||||||
|
// function, but not visible from Go (didn't match asmTEXT), so stop checking
|
||||||
|
fn = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if asmDATA.FindStringSubmatch(line) != nil {
|
||||||
|
fn = nil
|
||||||
|
}
|
||||||
|
if fn == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range asmUnnamedFP.FindAllStringSubmatch(line, -1) {
|
||||||
|
warnf("use of unnamed argument %s", m[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range asmNamedFP.FindAllStringSubmatch(line, -1) {
|
||||||
|
name := m[1]
|
||||||
|
off := 0
|
||||||
|
if m[2] != "" {
|
||||||
|
off, _ = strconv.Atoi(m[2])
|
||||||
|
}
|
||||||
|
v := fn.vars[name]
|
||||||
|
if v == nil {
|
||||||
|
// Allow argframe+0(FP).
|
||||||
|
if name == "argframe" && off == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
v = fn.varByOffset[off]
|
||||||
|
if v != nil {
|
||||||
|
warnf("unknown variable %s; offset %d is %s+%d(FP)", name, off, v.name, v.off)
|
||||||
|
} else {
|
||||||
|
warnf("unknown variable %s", name)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
asmCheckVar(warnf, fn, line, m[0], off, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// asmParseDecl parses a function decl for expected assembly variables.
|
||||||
|
func (f *File) asmParseDecl(decl *ast.FuncDecl) map[string]*asmFunc {
|
||||||
|
var (
|
||||||
|
arch *asmArch
|
||||||
|
fn *asmFunc
|
||||||
|
offset int
|
||||||
|
failed bool
|
||||||
|
)
|
||||||
|
|
||||||
|
addVar := func(outer string, v asmVar) {
|
||||||
|
if vo := fn.vars[outer]; vo != nil {
|
||||||
|
vo.inner = append(vo.inner, &v)
|
||||||
|
}
|
||||||
|
fn.vars[v.name] = &v
|
||||||
|
for i := 0; i < v.size; i++ {
|
||||||
|
fn.varByOffset[v.off+i] = &v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addParams := func(list []*ast.Field) {
|
||||||
|
for i, fld := range list {
|
||||||
|
// Determine alignment, size, and kind of type in declaration.
|
||||||
|
var align, size int
|
||||||
|
var kind asmKind
|
||||||
|
names := fld.Names
|
||||||
|
typ := f.gofmt(fld.Type)
|
||||||
|
switch t := fld.Type.(type) {
|
||||||
|
default:
|
||||||
|
switch typ {
|
||||||
|
default:
|
||||||
|
f.Warnf(fld.Type.Pos(), "unknown assembly argument type %s", typ)
|
||||||
|
failed = true
|
||||||
|
return
|
||||||
|
case "int8", "uint8", "byte", "bool":
|
||||||
|
size = 1
|
||||||
|
case "int16", "uint16":
|
||||||
|
size = 2
|
||||||
|
case "int32", "uint32", "float32":
|
||||||
|
size = 4
|
||||||
|
case "int64", "uint64", "float64":
|
||||||
|
align = arch.ptrSize
|
||||||
|
size = 8
|
||||||
|
case "int", "uint":
|
||||||
|
size = arch.intSize
|
||||||
|
case "uintptr", "iword", "Word", "Errno", "unsafe.Pointer":
|
||||||
|
size = arch.ptrSize
|
||||||
|
case "string":
|
||||||
|
size = arch.ptrSize * 2
|
||||||
|
align = arch.ptrSize
|
||||||
|
kind = asmString
|
||||||
|
}
|
||||||
|
case *ast.ChanType, *ast.FuncType, *ast.MapType, *ast.StarExpr:
|
||||||
|
size = arch.ptrSize
|
||||||
|
case *ast.InterfaceType:
|
||||||
|
align = arch.ptrSize
|
||||||
|
size = 2 * arch.ptrSize
|
||||||
|
if len(t.Methods.List) > 0 {
|
||||||
|
kind = asmInterface
|
||||||
|
} else {
|
||||||
|
kind = asmEmptyInterface
|
||||||
|
}
|
||||||
|
case *ast.ArrayType:
|
||||||
|
if t.Len == nil {
|
||||||
|
size = arch.ptrSize + 2*arch.intSize
|
||||||
|
align = arch.ptrSize
|
||||||
|
kind = asmSlice
|
||||||
|
break
|
||||||
|
}
|
||||||
|
f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ)
|
||||||
|
failed = true
|
||||||
|
case *ast.StructType:
|
||||||
|
f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ)
|
||||||
|
failed = true
|
||||||
|
}
|
||||||
|
if align == 0 {
|
||||||
|
align = size
|
||||||
|
}
|
||||||
|
if kind == 0 {
|
||||||
|
kind = asmKind(size)
|
||||||
|
}
|
||||||
|
offset += -offset & (align - 1)
|
||||||
|
|
||||||
|
// Create variable for each name being declared with this type.
|
||||||
|
if len(names) == 0 {
|
||||||
|
name := "unnamed"
|
||||||
|
if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 && &list[0] == &decl.Type.Results.List[0] && i == 0 {
|
||||||
|
// Assume assembly will refer to single unnamed result as r.
|
||||||
|
name = "ret"
|
||||||
|
}
|
||||||
|
names = []*ast.Ident{{Name: name}}
|
||||||
|
}
|
||||||
|
for _, id := range names {
|
||||||
|
name := id.Name
|
||||||
|
addVar("", asmVar{
|
||||||
|
name: name,
|
||||||
|
kind: kind,
|
||||||
|
typ: typ,
|
||||||
|
off: offset,
|
||||||
|
size: size,
|
||||||
|
})
|
||||||
|
switch kind {
|
||||||
|
case 8:
|
||||||
|
if arch.ptrSize == 4 {
|
||||||
|
w1, w2 := "lo", "hi"
|
||||||
|
if arch.bigEndian {
|
||||||
|
w1, w2 = w2, w1
|
||||||
|
}
|
||||||
|
addVar(name, asmVar{
|
||||||
|
name: name + "_" + w1,
|
||||||
|
kind: 4,
|
||||||
|
typ: "half " + typ,
|
||||||
|
off: offset,
|
||||||
|
size: 4,
|
||||||
|
})
|
||||||
|
addVar(name, asmVar{
|
||||||
|
name: name + "_" + w2,
|
||||||
|
kind: 4,
|
||||||
|
typ: "half " + typ,
|
||||||
|
off: offset + 4,
|
||||||
|
size: 4,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
case asmEmptyInterface:
|
||||||
|
addVar(name, asmVar{
|
||||||
|
name: name + "_type",
|
||||||
|
kind: asmKind(arch.ptrSize),
|
||||||
|
typ: "interface type",
|
||||||
|
off: offset,
|
||||||
|
size: arch.ptrSize,
|
||||||
|
})
|
||||||
|
addVar(name, asmVar{
|
||||||
|
name: name + "_data",
|
||||||
|
kind: asmKind(arch.ptrSize),
|
||||||
|
typ: "interface data",
|
||||||
|
off: offset + arch.ptrSize,
|
||||||
|
size: arch.ptrSize,
|
||||||
|
})
|
||||||
|
|
||||||
|
case asmInterface:
|
||||||
|
addVar(name, asmVar{
|
||||||
|
name: name + "_itable",
|
||||||
|
kind: asmKind(arch.ptrSize),
|
||||||
|
typ: "interface itable",
|
||||||
|
off: offset,
|
||||||
|
size: arch.ptrSize,
|
||||||
|
})
|
||||||
|
addVar(name, asmVar{
|
||||||
|
name: name + "_data",
|
||||||
|
kind: asmKind(arch.ptrSize),
|
||||||
|
typ: "interface data",
|
||||||
|
off: offset + arch.ptrSize,
|
||||||
|
size: arch.ptrSize,
|
||||||
|
})
|
||||||
|
|
||||||
|
case asmSlice:
|
||||||
|
addVar(name, asmVar{
|
||||||
|
name: name + "_base",
|
||||||
|
kind: asmKind(arch.ptrSize),
|
||||||
|
typ: "slice base",
|
||||||
|
off: offset,
|
||||||
|
size: arch.ptrSize,
|
||||||
|
})
|
||||||
|
addVar(name, asmVar{
|
||||||
|
name: name + "_len",
|
||||||
|
kind: asmKind(arch.intSize),
|
||||||
|
typ: "slice len",
|
||||||
|
off: offset + arch.ptrSize,
|
||||||
|
size: arch.intSize,
|
||||||
|
})
|
||||||
|
addVar(name, asmVar{
|
||||||
|
name: name + "_cap",
|
||||||
|
kind: asmKind(arch.intSize),
|
||||||
|
typ: "slice cap",
|
||||||
|
off: offset + arch.ptrSize + arch.intSize,
|
||||||
|
size: arch.intSize,
|
||||||
|
})
|
||||||
|
|
||||||
|
case asmString:
|
||||||
|
addVar(name, asmVar{
|
||||||
|
name: name + "_base",
|
||||||
|
kind: asmKind(arch.ptrSize),
|
||||||
|
typ: "string base",
|
||||||
|
off: offset,
|
||||||
|
size: arch.ptrSize,
|
||||||
|
})
|
||||||
|
addVar(name, asmVar{
|
||||||
|
name: name + "_len",
|
||||||
|
kind: asmKind(arch.intSize),
|
||||||
|
typ: "string len",
|
||||||
|
off: offset + arch.ptrSize,
|
||||||
|
size: arch.intSize,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
offset += size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m := make(map[string]*asmFunc)
|
||||||
|
for _, arch = range arches {
|
||||||
|
fn = &asmFunc{
|
||||||
|
arch: arch,
|
||||||
|
vars: make(map[string]*asmVar),
|
||||||
|
varByOffset: make(map[int]*asmVar),
|
||||||
|
}
|
||||||
|
offset = 0
|
||||||
|
addParams(decl.Type.Params.List)
|
||||||
|
if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 {
|
||||||
|
offset += -offset & (arch.ptrSize - 1)
|
||||||
|
addParams(decl.Type.Results.List)
|
||||||
|
}
|
||||||
|
fn.size = offset
|
||||||
|
m[arch.name] = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
if failed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// asmCheckVar checks a single variable reference.
|
||||||
|
func asmCheckVar(warnf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar) {
|
||||||
|
m := asmOpcode.FindStringSubmatch(line)
|
||||||
|
if m == nil {
|
||||||
|
warnf("cannot find assembly opcode")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine operand sizes from instruction.
|
||||||
|
// Typically the suffix suffices, but there are exceptions.
|
||||||
|
var src, dst, kind asmKind
|
||||||
|
op := m[1]
|
||||||
|
switch fn.arch.name + "." + op {
|
||||||
|
case "386.FMOVLP":
|
||||||
|
src, dst = 8, 4
|
||||||
|
case "arm.MOVD":
|
||||||
|
src = 8
|
||||||
|
case "arm.MOVW":
|
||||||
|
src = 4
|
||||||
|
case "arm.MOVH", "arm.MOVHU":
|
||||||
|
src = 2
|
||||||
|
case "arm.MOVB", "arm.MOVBU":
|
||||||
|
src = 1
|
||||||
|
default:
|
||||||
|
if fn.arch.name == "386" || fn.arch.name == "amd64" {
|
||||||
|
if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "D") || strings.HasSuffix(op, "DP")) {
|
||||||
|
// FMOVDP, FXCHD, etc
|
||||||
|
src = 8
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "F") || strings.HasSuffix(op, "FP")) {
|
||||||
|
// FMOVFP, FXCHF, etc
|
||||||
|
src = 4
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(op, "SD") {
|
||||||
|
// MOVSD, SQRTSD, etc
|
||||||
|
src = 8
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(op, "SS") {
|
||||||
|
// MOVSS, SQRTSS, etc
|
||||||
|
src = 4
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(op, "SET") {
|
||||||
|
// SETEQ, etc
|
||||||
|
src = 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch op[len(op)-1] {
|
||||||
|
case 'B':
|
||||||
|
src = 1
|
||||||
|
case 'W':
|
||||||
|
src = 2
|
||||||
|
case 'L':
|
||||||
|
src = 4
|
||||||
|
case 'D', 'Q':
|
||||||
|
src = 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dst == 0 {
|
||||||
|
dst = src
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine whether the match we're holding
|
||||||
|
// is the first or second argument.
|
||||||
|
if strings.Index(line, expr) > strings.Index(line, ",") {
|
||||||
|
kind = dst
|
||||||
|
} else {
|
||||||
|
kind = src
|
||||||
|
}
|
||||||
|
|
||||||
|
vk := v.kind
|
||||||
|
vt := v.typ
|
||||||
|
switch vk {
|
||||||
|
case asmInterface, asmEmptyInterface, asmString, asmSlice:
|
||||||
|
// allow reference to first word (pointer)
|
||||||
|
vk = v.inner[0].kind
|
||||||
|
vt = v.inner[0].typ
|
||||||
|
}
|
||||||
|
|
||||||
|
if off != v.off {
|
||||||
|
var inner bytes.Buffer
|
||||||
|
for i, vi := range v.inner {
|
||||||
|
if len(v.inner) > 1 {
|
||||||
|
fmt.Fprintf(&inner, ",")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&inner, " ")
|
||||||
|
if i == len(v.inner)-1 {
|
||||||
|
fmt.Fprintf(&inner, "or ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
|
||||||
|
}
|
||||||
|
warnf("invalid offset %s; expected %s+%d(FP)%s", expr, v.name, v.off, inner.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if kind != 0 && kind != vk {
|
||||||
|
var inner bytes.Buffer
|
||||||
|
if len(v.inner) > 0 {
|
||||||
|
fmt.Fprintf(&inner, " containing")
|
||||||
|
for i, vi := range v.inner {
|
||||||
|
if i > 0 && len(v.inner) > 2 {
|
||||||
|
fmt.Fprintf(&inner, ",")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&inner, " ")
|
||||||
|
if i > 0 && i == len(v.inner)-1 {
|
||||||
|
fmt.Fprintf(&inner, "and ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
warnf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vk, inner.String())
|
||||||
|
}
|
||||||
|
}
|
44
cmd/vet/assign.go
Normal file
44
cmd/vet/assign.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2013 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 contains the code to check for useless assignments.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: should also check for assignments to struct fields inside methods
|
||||||
|
// that are on T instead of *T.
|
||||||
|
|
||||||
|
// checkAssignStmt checks for assignments of the form "<expr> = <expr>".
|
||||||
|
// These are almost always useless, and even when they aren't they are usually a mistake.
|
||||||
|
func (f *File) checkAssignStmt(stmt *ast.AssignStmt) {
|
||||||
|
if !vet("assign") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if stmt.Tok != token.ASSIGN {
|
||||||
|
return // ignore :=
|
||||||
|
}
|
||||||
|
if len(stmt.Lhs) != len(stmt.Rhs) {
|
||||||
|
// If LHS and RHS have different cardinality, they can't be the same.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i, lhs := range stmt.Lhs {
|
||||||
|
rhs := stmt.Rhs[i]
|
||||||
|
if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) {
|
||||||
|
continue // short-circuit the heavy-weight gofmt check
|
||||||
|
}
|
||||||
|
le := f.gofmt(lhs)
|
||||||
|
re := f.gofmt(rhs)
|
||||||
|
if le == re {
|
||||||
|
f.Warnf(stmt.Pos(), "self-assignment of %s to %s", re, le)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
cmd/vet/atomic.go
Normal file
59
cmd/vet/atomic.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Copyright 2013 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 (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// checkAtomicAssignment walks the assignment statement checking for common
|
||||||
|
// mistaken usage of atomic package, such as: x = atomic.AddUint64(&x, 1)
|
||||||
|
func (f *File) checkAtomicAssignment(n *ast.AssignStmt) {
|
||||||
|
if !vet("atomic") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(n.Lhs) != len(n.Rhs) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, right := range n.Rhs {
|
||||||
|
call, ok := right.(*ast.CallExpr)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sel, ok := call.Fun.(*ast.SelectorExpr)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pkg, ok := sel.X.(*ast.Ident)
|
||||||
|
if !ok || pkg.Name != "atomic" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch sel.Sel.Name {
|
||||||
|
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
|
||||||
|
f.checkAtomicAddAssignment(n.Lhs[i], call)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkAtomicAddAssignment walks the atomic.Add* method calls checking for assigning the return value
|
||||||
|
// to the same variable being used in the operation
|
||||||
|
func (f *File) checkAtomicAddAssignment(left ast.Expr, call *ast.CallExpr) {
|
||||||
|
arg := call.Args[0]
|
||||||
|
broken := false
|
||||||
|
|
||||||
|
if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND {
|
||||||
|
broken = f.gofmt(left) == f.gofmt(uarg.X)
|
||||||
|
} else if star, ok := left.(*ast.StarExpr); ok {
|
||||||
|
broken = f.gofmt(star.X) == f.gofmt(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if broken {
|
||||||
|
f.Warn(left.Pos(), "direct assignment to atomic value")
|
||||||
|
}
|
||||||
|
}
|
91
cmd/vet/buildtag.go
Normal file
91
cmd/vet/buildtag.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// Copyright 2013 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 (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
nl = []byte("\n")
|
||||||
|
slashSlash = []byte("//")
|
||||||
|
plusBuild = []byte("+build")
|
||||||
|
)
|
||||||
|
|
||||||
|
// checkBuildTag checks that build tags are in the correct location and well-formed.
|
||||||
|
func checkBuildTag(name string, data []byte) {
|
||||||
|
if !vet("buildtags") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lines := bytes.SplitAfter(data, nl)
|
||||||
|
|
||||||
|
// Determine cutpoint where +build comments are no longer valid.
|
||||||
|
// They are valid in leading // comments in the file followed by
|
||||||
|
// a blank line.
|
||||||
|
var cutoff int
|
||||||
|
for i, line := range lines {
|
||||||
|
line = bytes.TrimSpace(line)
|
||||||
|
if len(line) == 0 {
|
||||||
|
cutoff = i
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if bytes.HasPrefix(line, slashSlash) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, line := range lines {
|
||||||
|
line = bytes.TrimSpace(line)
|
||||||
|
if !bytes.HasPrefix(line, slashSlash) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
text := bytes.TrimSpace(line[2:])
|
||||||
|
if bytes.HasPrefix(text, plusBuild) {
|
||||||
|
fields := bytes.Fields(text)
|
||||||
|
if !bytes.Equal(fields[0], plusBuild) {
|
||||||
|
// Comment is something like +buildasdf not +build.
|
||||||
|
fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if i >= cutoff {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s:%d: +build comment appears too late in file\n", name, i+1)
|
||||||
|
setExit(1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Check arguments.
|
||||||
|
Args:
|
||||||
|
for _, arg := range fields[1:] {
|
||||||
|
for _, elem := range strings.Split(string(arg), ",") {
|
||||||
|
if strings.HasPrefix(elem, "!!") {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s:%d: invalid double negative in build constraint: %s\n", name, i+1, arg)
|
||||||
|
setExit(1)
|
||||||
|
break Args
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(elem, "!") {
|
||||||
|
elem = elem[1:]
|
||||||
|
}
|
||||||
|
for _, c := range elem {
|
||||||
|
if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s:%d: invalid non-alphanumeric build constraint: %s\n", name, i+1, arg)
|
||||||
|
setExit(1)
|
||||||
|
break Args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Comment with +build but not at beginning.
|
||||||
|
if bytes.Contains(line, plusBuild) && i < cutoff {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
280
cmd/vet/deadcode.go
Normal file
280
cmd/vet/deadcode.go
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
// Check for syntactically unreachable code.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
type deadState struct {
|
||||||
|
f *File
|
||||||
|
hasBreak map[ast.Stmt]bool
|
||||||
|
hasGoto map[string]bool
|
||||||
|
labels map[string]ast.Stmt
|
||||||
|
breakTarget ast.Stmt
|
||||||
|
|
||||||
|
reachable bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkUnreachable checks a function body for dead code.
|
||||||
|
func (f *File) checkUnreachable(body *ast.BlockStmt) {
|
||||||
|
if !vet("unreachable") || body == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
d := &deadState{
|
||||||
|
f: f,
|
||||||
|
hasBreak: make(map[ast.Stmt]bool),
|
||||||
|
hasGoto: make(map[string]bool),
|
||||||
|
labels: make(map[string]ast.Stmt),
|
||||||
|
}
|
||||||
|
|
||||||
|
d.findLabels(body)
|
||||||
|
|
||||||
|
d.reachable = true
|
||||||
|
d.findDead(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// findLabels gathers information about the labels defined and used by stmt
|
||||||
|
// and about which statements break, whether a label is involved or not.
|
||||||
|
func (d *deadState) findLabels(stmt ast.Stmt) {
|
||||||
|
switch x := stmt.(type) {
|
||||||
|
default:
|
||||||
|
d.f.Warnf(x.Pos(), "internal error in findLabels: unexpected statement %T", x)
|
||||||
|
|
||||||
|
case *ast.AssignStmt,
|
||||||
|
*ast.BadStmt,
|
||||||
|
*ast.DeclStmt,
|
||||||
|
*ast.DeferStmt,
|
||||||
|
*ast.EmptyStmt,
|
||||||
|
*ast.ExprStmt,
|
||||||
|
*ast.GoStmt,
|
||||||
|
*ast.IncDecStmt,
|
||||||
|
*ast.ReturnStmt,
|
||||||
|
*ast.SendStmt:
|
||||||
|
// no statements inside
|
||||||
|
|
||||||
|
case *ast.BlockStmt:
|
||||||
|
for _, stmt := range x.List {
|
||||||
|
d.findLabels(stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.BranchStmt:
|
||||||
|
switch x.Tok {
|
||||||
|
case token.GOTO:
|
||||||
|
d.hasGoto[x.Label.Name] = true
|
||||||
|
|
||||||
|
case token.BREAK:
|
||||||
|
stmt := d.breakTarget
|
||||||
|
if x.Label != nil {
|
||||||
|
stmt = d.labels[x.Label.Name]
|
||||||
|
}
|
||||||
|
if stmt != nil {
|
||||||
|
d.hasBreak[stmt] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.IfStmt:
|
||||||
|
d.findLabels(x.Body)
|
||||||
|
if x.Else != nil {
|
||||||
|
d.findLabels(x.Else)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.LabeledStmt:
|
||||||
|
d.labels[x.Label.Name] = x.Stmt
|
||||||
|
d.findLabels(x.Stmt)
|
||||||
|
|
||||||
|
// These cases are all the same, but the x.Body only works
|
||||||
|
// when the specific type of x is known, so the cases cannot
|
||||||
|
// be merged.
|
||||||
|
case *ast.ForStmt:
|
||||||
|
outer := d.breakTarget
|
||||||
|
d.breakTarget = x
|
||||||
|
d.findLabels(x.Body)
|
||||||
|
d.breakTarget = outer
|
||||||
|
|
||||||
|
case *ast.RangeStmt:
|
||||||
|
outer := d.breakTarget
|
||||||
|
d.breakTarget = x
|
||||||
|
d.findLabels(x.Body)
|
||||||
|
d.breakTarget = outer
|
||||||
|
|
||||||
|
case *ast.SelectStmt:
|
||||||
|
outer := d.breakTarget
|
||||||
|
d.breakTarget = x
|
||||||
|
d.findLabels(x.Body)
|
||||||
|
d.breakTarget = outer
|
||||||
|
|
||||||
|
case *ast.SwitchStmt:
|
||||||
|
outer := d.breakTarget
|
||||||
|
d.breakTarget = x
|
||||||
|
d.findLabels(x.Body)
|
||||||
|
d.breakTarget = outer
|
||||||
|
|
||||||
|
case *ast.TypeSwitchStmt:
|
||||||
|
outer := d.breakTarget
|
||||||
|
d.breakTarget = x
|
||||||
|
d.findLabels(x.Body)
|
||||||
|
d.breakTarget = outer
|
||||||
|
|
||||||
|
case *ast.CommClause:
|
||||||
|
for _, stmt := range x.Body {
|
||||||
|
d.findLabels(stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.CaseClause:
|
||||||
|
for _, stmt := range x.Body {
|
||||||
|
d.findLabels(stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// findDead walks the statement looking for dead code.
|
||||||
|
// If d.reachable is false on entry, stmt itself is dead.
|
||||||
|
// When findDead returns, d.reachable tells whether the
|
||||||
|
// statement following stmt is reachable.
|
||||||
|
func (d *deadState) findDead(stmt ast.Stmt) {
|
||||||
|
// Is this a labeled goto target?
|
||||||
|
// If so, assume it is reachable due to the goto.
|
||||||
|
// This is slightly conservative, in that we don't
|
||||||
|
// check that the goto is reachable, so
|
||||||
|
// L: goto L
|
||||||
|
// will not provoke a warning.
|
||||||
|
// But it's good enough.
|
||||||
|
if x, isLabel := stmt.(*ast.LabeledStmt); isLabel && d.hasGoto[x.Label.Name] {
|
||||||
|
d.reachable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !d.reachable {
|
||||||
|
switch stmt.(type) {
|
||||||
|
case *ast.EmptyStmt:
|
||||||
|
// do not warn about unreachable empty statements
|
||||||
|
default:
|
||||||
|
d.f.Warnf(stmt.Pos(), "unreachable code")
|
||||||
|
d.reachable = true // silence error about next statement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch x := stmt.(type) {
|
||||||
|
default:
|
||||||
|
d.f.Warnf(x.Pos(), "internal error in findDead: unexpected statement %T", x)
|
||||||
|
|
||||||
|
case *ast.AssignStmt,
|
||||||
|
*ast.BadStmt,
|
||||||
|
*ast.DeclStmt,
|
||||||
|
*ast.DeferStmt,
|
||||||
|
*ast.EmptyStmt,
|
||||||
|
*ast.GoStmt,
|
||||||
|
*ast.IncDecStmt,
|
||||||
|
*ast.SendStmt:
|
||||||
|
// no control flow
|
||||||
|
|
||||||
|
case *ast.BlockStmt:
|
||||||
|
for _, stmt := range x.List {
|
||||||
|
d.findDead(stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.BranchStmt:
|
||||||
|
switch x.Tok {
|
||||||
|
case token.BREAK, token.GOTO, token.FALLTHROUGH:
|
||||||
|
d.reachable = false
|
||||||
|
case token.CONTINUE:
|
||||||
|
// NOTE: We accept "continue" statements as terminating.
|
||||||
|
// They are not necessary in the spec definition of terminating,
|
||||||
|
// because a continue statement cannot be the final statement
|
||||||
|
// before a return. But for the more general problem of syntactically
|
||||||
|
// identifying dead code, continue redirects control flow just
|
||||||
|
// like the other terminating statements.
|
||||||
|
d.reachable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ExprStmt:
|
||||||
|
// Call to panic?
|
||||||
|
call, ok := x.X.(*ast.CallExpr)
|
||||||
|
if ok {
|
||||||
|
name, ok := call.Fun.(*ast.Ident)
|
||||||
|
if ok && name.Name == "panic" && name.Obj == nil {
|
||||||
|
d.reachable = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ForStmt:
|
||||||
|
d.findDead(x.Body)
|
||||||
|
d.reachable = x.Cond != nil || d.hasBreak[x]
|
||||||
|
|
||||||
|
case *ast.IfStmt:
|
||||||
|
d.findDead(x.Body)
|
||||||
|
if x.Else != nil {
|
||||||
|
r := d.reachable
|
||||||
|
d.reachable = true
|
||||||
|
d.findDead(x.Else)
|
||||||
|
d.reachable = d.reachable || r
|
||||||
|
} else {
|
||||||
|
// might not have executed if statement
|
||||||
|
d.reachable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.LabeledStmt:
|
||||||
|
d.findDead(x.Stmt)
|
||||||
|
|
||||||
|
case *ast.RangeStmt:
|
||||||
|
d.findDead(x.Body)
|
||||||
|
d.reachable = true
|
||||||
|
|
||||||
|
case *ast.ReturnStmt:
|
||||||
|
d.reachable = false
|
||||||
|
|
||||||
|
case *ast.SelectStmt:
|
||||||
|
// NOTE: Unlike switch and type switch below, we don't care
|
||||||
|
// whether a select has a default, because a select without a
|
||||||
|
// default blocks until one of the cases can run. That's different
|
||||||
|
// from a switch without a default, which behaves like it has
|
||||||
|
// a default with an empty body.
|
||||||
|
anyReachable := false
|
||||||
|
for _, comm := range x.Body.List {
|
||||||
|
d.reachable = true
|
||||||
|
for _, stmt := range comm.(*ast.CommClause).Body {
|
||||||
|
d.findDead(stmt)
|
||||||
|
}
|
||||||
|
anyReachable = anyReachable || d.reachable
|
||||||
|
}
|
||||||
|
d.reachable = anyReachable || d.hasBreak[x]
|
||||||
|
|
||||||
|
case *ast.SwitchStmt:
|
||||||
|
anyReachable := false
|
||||||
|
hasDefault := false
|
||||||
|
for _, cas := range x.Body.List {
|
||||||
|
cc := cas.(*ast.CaseClause)
|
||||||
|
if cc.List == nil {
|
||||||
|
hasDefault = true
|
||||||
|
}
|
||||||
|
d.reachable = true
|
||||||
|
for _, stmt := range cc.Body {
|
||||||
|
d.findDead(stmt)
|
||||||
|
}
|
||||||
|
anyReachable = anyReachable || d.reachable
|
||||||
|
}
|
||||||
|
d.reachable = anyReachable || d.hasBreak[x] || !hasDefault
|
||||||
|
|
||||||
|
case *ast.TypeSwitchStmt:
|
||||||
|
anyReachable := false
|
||||||
|
hasDefault := false
|
||||||
|
for _, cas := range x.Body.List {
|
||||||
|
cc := cas.(*ast.CaseClause)
|
||||||
|
if cc.List == nil {
|
||||||
|
hasDefault = true
|
||||||
|
}
|
||||||
|
d.reachable = true
|
||||||
|
for _, stmt := range cc.Body {
|
||||||
|
d.findDead(stmt)
|
||||||
|
}
|
||||||
|
anyReachable = anyReachable || d.reachable
|
||||||
|
}
|
||||||
|
d.reachable = anyReachable || d.hasBreak[x] || !hasDefault
|
||||||
|
}
|
||||||
|
}
|
76
cmd/vet/doc.go
Normal file
76
cmd/vet/doc.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Copyright 2010 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.
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Vet examines Go source code and reports suspicious constructs, such as Printf
|
||||||
|
calls whose arguments do not align with the format string. Vet uses heuristics
|
||||||
|
that do not guarantee all reports are genuine problems, but it can find errors
|
||||||
|
not caught by the compilers.
|
||||||
|
|
||||||
|
Its exit code is 2 for erroneous invocation of the tool, 1 if a
|
||||||
|
problem was reported, and 0 otherwise. Note that the tool does not
|
||||||
|
check every possible problem and depends on unreliable heuristics
|
||||||
|
so it should be used as guidance only, not as a firm indicator of
|
||||||
|
program correctness.
|
||||||
|
|
||||||
|
By default all checks are performed, but if explicit flags are provided, only
|
||||||
|
those identified by the flags are performed.
|
||||||
|
|
||||||
|
Available checks:
|
||||||
|
|
||||||
|
1. Printf family, flag -printf
|
||||||
|
|
||||||
|
Suspicious calls to functions in the Printf family, including any functions
|
||||||
|
with these names:
|
||||||
|
Print Printf Println
|
||||||
|
Fprint Fprintf Fprintln
|
||||||
|
Sprint Sprintf Sprintln
|
||||||
|
Error Errorf
|
||||||
|
Fatal Fatalf
|
||||||
|
Panic Panicf Panicln
|
||||||
|
If the function name ends with an 'f', the function is assumed to take
|
||||||
|
a format descriptor string in the manner of fmt.Printf. If not, vet
|
||||||
|
complains about arguments that look like format descriptor strings.
|
||||||
|
|
||||||
|
It also checks for errors such as using a Writer as the first argument of
|
||||||
|
Printf.
|
||||||
|
|
||||||
|
2. Methods, flag -methods
|
||||||
|
|
||||||
|
Non-standard signatures for methods with familiar names, including:
|
||||||
|
Format GobEncode GobDecode MarshalJSON MarshalXML
|
||||||
|
Peek ReadByte ReadFrom ReadRune Scan Seek
|
||||||
|
UnmarshalJSON UnreadByte UnreadRune WriteByte
|
||||||
|
WriteTo
|
||||||
|
|
||||||
|
3. Struct tags, flag -structtags
|
||||||
|
|
||||||
|
Struct tags that do not follow the format understood by reflect.StructTag.Get.
|
||||||
|
|
||||||
|
4. Untagged composite literals, flag -composites
|
||||||
|
|
||||||
|
Composite struct literals that do not use the type-tagged syntax.
|
||||||
|
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
go tool vet [flag] [file.go ...]
|
||||||
|
go tool vet [flag] [directory ...] # Scan all .go files under directory, recursively
|
||||||
|
|
||||||
|
The other flags are:
|
||||||
|
-v
|
||||||
|
Verbose mode
|
||||||
|
-printfuncs
|
||||||
|
A comma-separated list of print-like functions to supplement
|
||||||
|
the standard list. Each entry is in the form Name:N where N
|
||||||
|
is the zero-based argument position of the first argument
|
||||||
|
involved in the print: either the format or the first print
|
||||||
|
argument for non-formatted prints. For example,
|
||||||
|
if you have Warn and Warnf functions that take an
|
||||||
|
io.Writer as their first argument, like Fprintf,
|
||||||
|
-printfuncs=Warn:1,Warnf:1
|
||||||
|
|
||||||
|
*/
|
||||||
|
package main
|
422
cmd/vet/main.go
Normal file
422
cmd/vet/main.go
Normal file
@ -0,0 +1,422 @@
|
|||||||
|
// Copyright 2010 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.
|
||||||
|
|
||||||
|
// Vet is a simple checker for static errors in Go source code.
|
||||||
|
// See doc.go for more information.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
|
"go/parser"
|
||||||
|
"go/printer"
|
||||||
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var verbose = flag.Bool("v", false, "verbose")
|
||||||
|
var exitCode = 0
|
||||||
|
|
||||||
|
// Flags to control which checks to perform. "all" is set to true here, and disabled later if
|
||||||
|
// a flag is set explicitly.
|
||||||
|
var report = map[string]*bool{
|
||||||
|
"all": flag.Bool("all", true, "check everything; disabled if any explicit check is requested"),
|
||||||
|
"asmdecl": flag.Bool("asmdecl", false, "check assembly against Go declarations"),
|
||||||
|
"assign": flag.Bool("assign", false, "check for useless assignments"),
|
||||||
|
"atomic": flag.Bool("atomic", false, "check for common mistaken usages of the sync/atomic package"),
|
||||||
|
"buildtags": flag.Bool("buildtags", false, "check that +build tags are valid"),
|
||||||
|
"composites": flag.Bool("composites", false, "check that composite literals used type-tagged elements"),
|
||||||
|
"methods": flag.Bool("methods", false, "check that canonically named methods are canonically defined"),
|
||||||
|
"printf": flag.Bool("printf", false, "check printf-like invocations"),
|
||||||
|
"rangeloops": flag.Bool("rangeloops", false, "check that range loop variables are used correctly"),
|
||||||
|
"structtags": flag.Bool("structtags", false, "check that struct field tags have canonical format"),
|
||||||
|
"unreachable": flag.Bool("unreachable", false, "check for unreachable code"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// vet tells whether to report errors for the named check, a flag name.
|
||||||
|
func vet(name string) bool {
|
||||||
|
return *report["all"] || *report[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// setExit sets the value for os.Exit when it is called, later. It
|
||||||
|
// remembers the highest value.
|
||||||
|
func setExit(err int) {
|
||||||
|
if err > exitCode {
|
||||||
|
exitCode = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage is a replacement usage function for the flags package.
|
||||||
|
func Usage() {
|
||||||
|
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
|
||||||
|
fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n")
|
||||||
|
fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n")
|
||||||
|
flag.PrintDefaults()
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// File is a wrapper for the state of a file used in the parser.
|
||||||
|
// The parse tree walkers are all methods of this type.
|
||||||
|
type File struct {
|
||||||
|
pkg *Package
|
||||||
|
fset *token.FileSet
|
||||||
|
name string
|
||||||
|
content []byte
|
||||||
|
file *ast.File
|
||||||
|
b bytes.Buffer // for use by methods
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Usage = Usage
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// If a check is named explicitly, turn off the 'all' flag.
|
||||||
|
for name, ptr := range report {
|
||||||
|
if name != "all" && *ptr {
|
||||||
|
*report["all"] = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *printfuncs != "" {
|
||||||
|
for _, name := range strings.Split(*printfuncs, ",") {
|
||||||
|
if len(name) == 0 {
|
||||||
|
flag.Usage()
|
||||||
|
}
|
||||||
|
skip := 0
|
||||||
|
if colon := strings.LastIndex(name, ":"); colon > 0 {
|
||||||
|
var err error
|
||||||
|
skip, err = strconv.Atoi(name[colon+1:])
|
||||||
|
if err != nil {
|
||||||
|
errorf(`illegal format for "Func:N" argument %q; %s`, name, err)
|
||||||
|
}
|
||||||
|
name = name[:colon]
|
||||||
|
}
|
||||||
|
name = strings.ToLower(name)
|
||||||
|
if name[len(name)-1] == 'f' {
|
||||||
|
printfList[name] = skip
|
||||||
|
} else {
|
||||||
|
printList[name] = skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if flag.NArg() == 0 {
|
||||||
|
Usage()
|
||||||
|
}
|
||||||
|
dirs := false
|
||||||
|
files := false
|
||||||
|
for _, name := range flag.Args() {
|
||||||
|
// Is it a directory?
|
||||||
|
fi, err := os.Stat(name)
|
||||||
|
if err != nil {
|
||||||
|
warnf("error walking tree: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fi.IsDir() {
|
||||||
|
dirs = true
|
||||||
|
} else {
|
||||||
|
files = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dirs && files {
|
||||||
|
Usage()
|
||||||
|
}
|
||||||
|
if dirs {
|
||||||
|
for _, name := range flag.Args() {
|
||||||
|
walkDir(name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
doPackage(flag.Args())
|
||||||
|
os.Exit(exitCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefixDirectory places the directory name on the beginning of each name in the list.
|
||||||
|
func prefixDirectory(directory string, names []string) {
|
||||||
|
if directory != "." {
|
||||||
|
for i, name := range names {
|
||||||
|
names[i] = filepath.Join(directory, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// doPackageDir analyzes the single package found in the directory, if there is one,
|
||||||
|
// plus a test package, if there is one.
|
||||||
|
func doPackageDir(directory string) {
|
||||||
|
pkg, err := build.Default.ImportDir(directory, 0)
|
||||||
|
if err != nil {
|
||||||
|
// If it's just that there are no go source files, that's fine.
|
||||||
|
if _, nogo := err.(*build.NoGoError); nogo {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Non-fatal: we are doing a recursive walk and there may be other directories.
|
||||||
|
warnf("cannot process directory %s: %s", directory, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var names []string
|
||||||
|
names = append(names, pkg.GoFiles...)
|
||||||
|
names = append(names, pkg.CgoFiles...)
|
||||||
|
names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package.
|
||||||
|
names = append(names, pkg.SFiles...)
|
||||||
|
prefixDirectory(directory, names)
|
||||||
|
doPackage(names)
|
||||||
|
// Is there also a "foo_test" package? If so, do that one as well.
|
||||||
|
if len(pkg.XTestGoFiles) > 0 {
|
||||||
|
names = pkg.XTestGoFiles
|
||||||
|
prefixDirectory(directory, names)
|
||||||
|
doPackage(names)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Package struct {
|
||||||
|
types map[ast.Expr]Type
|
||||||
|
values map[ast.Expr]ExactValue
|
||||||
|
files []*File
|
||||||
|
}
|
||||||
|
|
||||||
|
// doPackage analyzes the single package constructed from the named files.
|
||||||
|
func doPackage(names []string) {
|
||||||
|
var files []*File
|
||||||
|
var astFiles []*ast.File
|
||||||
|
fs := token.NewFileSet()
|
||||||
|
for _, name := range names {
|
||||||
|
f, err := os.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
// Warn but continue to next package.
|
||||||
|
warnf("%s: %s", name, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
data, err := ioutil.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
warnf("%s: %s", name, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
checkBuildTag(name, data)
|
||||||
|
var parsedFile *ast.File
|
||||||
|
if strings.HasSuffix(name, ".go") {
|
||||||
|
parsedFile, err = parser.ParseFile(fs, name, bytes.NewReader(data), 0)
|
||||||
|
if err != nil {
|
||||||
|
warnf("%s: %s", name, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
astFiles = append(astFiles, parsedFile)
|
||||||
|
}
|
||||||
|
files = append(files, &File{fset: fs, content: data, name: name, file: parsedFile})
|
||||||
|
}
|
||||||
|
pkg := new(Package)
|
||||||
|
pkg.files = files
|
||||||
|
// Type check the package.
|
||||||
|
err := pkg.check(fs, astFiles)
|
||||||
|
if err != nil && *verbose {
|
||||||
|
warnf("%s", err)
|
||||||
|
}
|
||||||
|
for _, file := range files {
|
||||||
|
file.pkg = pkg
|
||||||
|
if file.file != nil {
|
||||||
|
file.walkFile(file.name, file.file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
asmCheck(pkg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func visit(path string, f os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
warnf("walk error: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// One package per directory. Ignore the files themselves.
|
||||||
|
if !f.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
doPackageDir(path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkg *Package) hasFileWithSuffix(suffix string) bool {
|
||||||
|
for _, f := range pkg.files {
|
||||||
|
if strings.HasSuffix(f.name, suffix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// walkDir recursively walks the tree looking for Go packages.
|
||||||
|
func walkDir(root string) {
|
||||||
|
filepath.Walk(root, visit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// errorf formats the error to standard error, adding program
|
||||||
|
// identification and a newline, and exits.
|
||||||
|
func errorf(format string, args ...interface{}) {
|
||||||
|
fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// warnf formats the error to standard error, adding program
|
||||||
|
// identification and a newline, but does not exit.
|
||||||
|
func warnf(format string, args ...interface{}) {
|
||||||
|
fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
|
||||||
|
setExit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println is fmt.Println guarded by -v.
|
||||||
|
func Println(args ...interface{}) {
|
||||||
|
if !*verbose {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf is fmt.Printf guarded by -v.
|
||||||
|
func Printf(format string, args ...interface{}) {
|
||||||
|
if !*verbose {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf(format+"\n", args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bad reports an error and sets the exit code..
|
||||||
|
func (f *File) Bad(pos token.Pos, args ...interface{}) {
|
||||||
|
f.Warn(pos, args...)
|
||||||
|
setExit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Badf reports a formatted error and sets the exit code.
|
||||||
|
func (f *File) Badf(pos token.Pos, format string, args ...interface{}) {
|
||||||
|
f.Warnf(pos, format, args...)
|
||||||
|
setExit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) loc(pos token.Pos) string {
|
||||||
|
if pos == token.NoPos {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
// Do not print columns. Because the pos often points to the start of an
|
||||||
|
// expression instead of the inner part with the actual error, the
|
||||||
|
// precision can mislead.
|
||||||
|
posn := f.fset.Position(pos)
|
||||||
|
return fmt.Sprintf("%s:%d: ", posn.Filename, posn.Line)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn reports an error but does not set the exit code.
|
||||||
|
func (f *File) Warn(pos token.Pos, args ...interface{}) {
|
||||||
|
fmt.Fprint(os.Stderr, f.loc(pos)+fmt.Sprintln(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnf reports a formatted error but does not set the exit code.
|
||||||
|
func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) {
|
||||||
|
fmt.Fprintf(os.Stderr, f.loc(pos)+format+"\n", args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// walkFile walks the file's tree.
|
||||||
|
func (f *File) walkFile(name string, file *ast.File) {
|
||||||
|
Println("Checking file", name)
|
||||||
|
ast.Walk(f, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visit implements the ast.Visitor interface.
|
||||||
|
func (f *File) Visit(node ast.Node) ast.Visitor {
|
||||||
|
switch n := node.(type) {
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
f.walkAssignStmt(n)
|
||||||
|
case *ast.CallExpr:
|
||||||
|
f.walkCallExpr(n)
|
||||||
|
case *ast.CompositeLit:
|
||||||
|
f.walkCompositeLit(n)
|
||||||
|
case *ast.Field:
|
||||||
|
f.walkFieldTag(n)
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
f.walkFuncDecl(n)
|
||||||
|
case *ast.FuncLit:
|
||||||
|
f.walkFuncLit(n)
|
||||||
|
case *ast.InterfaceType:
|
||||||
|
f.walkInterfaceType(n)
|
||||||
|
case *ast.RangeStmt:
|
||||||
|
f.walkRangeStmt(n)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// walkAssignStmt walks an assignment statement
|
||||||
|
func (f *File) walkAssignStmt(stmt *ast.AssignStmt) {
|
||||||
|
f.checkAssignStmt(stmt)
|
||||||
|
f.checkAtomicAssignment(stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// walkCall walks a call expression.
|
||||||
|
func (f *File) walkCall(call *ast.CallExpr, name string) {
|
||||||
|
f.checkFmtPrintfCall(call, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// walkCallExpr walks a call expression.
|
||||||
|
func (f *File) walkCallExpr(call *ast.CallExpr) {
|
||||||
|
switch x := call.Fun.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
f.walkCall(call, x.Name)
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
f.walkCall(call, x.Sel.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// walkCompositeLit walks a composite literal.
|
||||||
|
func (f *File) walkCompositeLit(c *ast.CompositeLit) {
|
||||||
|
f.checkUntaggedLiteral(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// walkFieldTag walks a struct field tag.
|
||||||
|
func (f *File) walkFieldTag(field *ast.Field) {
|
||||||
|
if field.Tag == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f.checkCanonicalFieldTag(field)
|
||||||
|
}
|
||||||
|
|
||||||
|
// walkMethod walks the method's signature.
|
||||||
|
func (f *File) walkMethod(id *ast.Ident, t *ast.FuncType) {
|
||||||
|
f.checkCanonicalMethod(id, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// walkFuncDecl walks a function declaration.
|
||||||
|
func (f *File) walkFuncDecl(d *ast.FuncDecl) {
|
||||||
|
f.checkUnreachable(d.Body)
|
||||||
|
if d.Recv != nil {
|
||||||
|
f.walkMethod(d.Name, d.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// walkFuncLit walks a function literal.
|
||||||
|
func (f *File) walkFuncLit(x *ast.FuncLit) {
|
||||||
|
f.checkUnreachable(x.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// walkInterfaceType walks the method signatures of an interface.
|
||||||
|
func (f *File) walkInterfaceType(t *ast.InterfaceType) {
|
||||||
|
for _, field := range t.Methods.List {
|
||||||
|
for _, id := range field.Names {
|
||||||
|
f.walkMethod(id, field.Type.(*ast.FuncType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// walkRangeStmt walks a range statement.
|
||||||
|
func (f *File) walkRangeStmt(n *ast.RangeStmt) {
|
||||||
|
checkRangeLoop(f, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gofmt returns a string representation of the expression.
|
||||||
|
func (f *File) gofmt(x ast.Expr) string {
|
||||||
|
f.b.Reset()
|
||||||
|
printer.Fprint(&f.b, f.fset, x)
|
||||||
|
return f.b.String()
|
||||||
|
}
|
162
cmd/vet/method.go
Normal file
162
cmd/vet/method.go
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
// Copyright 2010 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 contains the code to check canonical methods.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/printer"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MethodSig struct {
|
||||||
|
args []string
|
||||||
|
results []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// canonicalMethods lists the input and output types for Go methods
|
||||||
|
// that are checked using dynamic interface checks. Because the
|
||||||
|
// checks are dynamic, such methods would not cause a compile error
|
||||||
|
// if they have the wrong signature: instead the dynamic check would
|
||||||
|
// fail, sometimes mysteriously. If a method is found with a name listed
|
||||||
|
// here but not the input/output types listed here, vet complains.
|
||||||
|
//
|
||||||
|
// A few of the canonical methods have very common names.
|
||||||
|
// For example, a type might implement a Scan method that
|
||||||
|
// has nothing to do with fmt.Scanner, but we still want to check
|
||||||
|
// the methods that are intended to implement fmt.Scanner.
|
||||||
|
// To do that, the arguments that have a = prefix are treated as
|
||||||
|
// signals that the canonical meaning is intended: if a Scan
|
||||||
|
// method doesn't have a fmt.ScanState as its first argument,
|
||||||
|
// we let it go. But if it does have a fmt.ScanState, then the
|
||||||
|
// rest has to match.
|
||||||
|
var canonicalMethods = map[string]MethodSig{
|
||||||
|
// "Flush": {{}, {"error"}}, // http.Flusher and jpeg.writer conflict
|
||||||
|
"Format": {[]string{"=fmt.State", "rune"}, []string{}}, // fmt.Formatter
|
||||||
|
"GobDecode": {[]string{"[]byte"}, []string{"error"}}, // gob.GobDecoder
|
||||||
|
"GobEncode": {[]string{}, []string{"[]byte", "error"}}, // gob.GobEncoder
|
||||||
|
"MarshalJSON": {[]string{}, []string{"[]byte", "error"}}, // json.Marshaler
|
||||||
|
"MarshalXML": {[]string{}, []string{"[]byte", "error"}}, // xml.Marshaler
|
||||||
|
"Peek": {[]string{"=int"}, []string{"[]byte", "error"}}, // image.reader (matching bufio.Reader)
|
||||||
|
"ReadByte": {[]string{}, []string{"byte", "error"}}, // io.ByteReader
|
||||||
|
"ReadFrom": {[]string{"=io.Reader"}, []string{"int64", "error"}}, // io.ReaderFrom
|
||||||
|
"ReadRune": {[]string{}, []string{"rune", "int", "error"}}, // io.RuneReader
|
||||||
|
"Scan": {[]string{"=fmt.ScanState", "rune"}, []string{"error"}}, // fmt.Scanner
|
||||||
|
"Seek": {[]string{"=int64", "int"}, []string{"int64", "error"}}, // io.Seeker
|
||||||
|
"UnmarshalJSON": {[]string{"[]byte"}, []string{"error"}}, // json.Unmarshaler
|
||||||
|
"UnreadByte": {[]string{}, []string{"error"}},
|
||||||
|
"UnreadRune": {[]string{}, []string{"error"}},
|
||||||
|
"WriteByte": {[]string{"byte"}, []string{"error"}}, // jpeg.writer (matching bufio.Writer)
|
||||||
|
"WriteTo": {[]string{"=io.Writer"}, []string{"int64", "error"}}, // io.WriterTo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) checkCanonicalMethod(id *ast.Ident, t *ast.FuncType) {
|
||||||
|
if !vet("methods") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Expected input/output.
|
||||||
|
expect, ok := canonicalMethods[id.Name]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actual input/output
|
||||||
|
args := typeFlatten(t.Params.List)
|
||||||
|
var results []ast.Expr
|
||||||
|
if t.Results != nil {
|
||||||
|
results = typeFlatten(t.Results.List)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the =s (if any) all match?
|
||||||
|
if !f.matchParams(expect.args, args, "=") || !f.matchParams(expect.results, results, "=") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything must match.
|
||||||
|
if !f.matchParams(expect.args, args, "") || !f.matchParams(expect.results, results, "") {
|
||||||
|
expectFmt := id.Name + "(" + argjoin(expect.args) + ")"
|
||||||
|
if len(expect.results) == 1 {
|
||||||
|
expectFmt += " " + argjoin(expect.results)
|
||||||
|
} else if len(expect.results) > 1 {
|
||||||
|
expectFmt += " (" + argjoin(expect.results) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
f.b.Reset()
|
||||||
|
if err := printer.Fprint(&f.b, f.fset, t); err != nil {
|
||||||
|
fmt.Fprintf(&f.b, "<%s>", err)
|
||||||
|
}
|
||||||
|
actual := f.b.String()
|
||||||
|
actual = strings.TrimPrefix(actual, "func")
|
||||||
|
actual = id.Name + actual
|
||||||
|
|
||||||
|
f.Badf(id.Pos(), "method %s should have signature %s", actual, expectFmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func argjoin(x []string) string {
|
||||||
|
y := make([]string, len(x))
|
||||||
|
for i, s := range x {
|
||||||
|
if s[0] == '=' {
|
||||||
|
s = s[1:]
|
||||||
|
}
|
||||||
|
y[i] = s
|
||||||
|
}
|
||||||
|
return strings.Join(y, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn parameter list into slice of types
|
||||||
|
// (in the ast, types are Exprs).
|
||||||
|
// Have to handle f(int, bool) and f(x, y, z int)
|
||||||
|
// so not a simple 1-to-1 conversion.
|
||||||
|
func typeFlatten(l []*ast.Field) []ast.Expr {
|
||||||
|
var t []ast.Expr
|
||||||
|
for _, f := range l {
|
||||||
|
if len(f.Names) == 0 {
|
||||||
|
t = append(t, f.Type)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _ = range f.Names {
|
||||||
|
t = append(t, f.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does each type in expect with the given prefix match the corresponding type in actual?
|
||||||
|
func (f *File) matchParams(expect []string, actual []ast.Expr, prefix string) bool {
|
||||||
|
for i, x := range expect {
|
||||||
|
if !strings.HasPrefix(x, prefix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if i >= len(actual) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !f.matchParamType(x, actual[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if prefix == "" && len(actual) > len(expect) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does this one type match?
|
||||||
|
func (f *File) matchParamType(expect string, actual ast.Expr) bool {
|
||||||
|
if strings.HasPrefix(expect, "=") {
|
||||||
|
expect = expect[1:]
|
||||||
|
}
|
||||||
|
// Strip package name if we're in that package.
|
||||||
|
if n := len(f.file.Name.Name); len(expect) > n && expect[:n] == f.file.Name.Name && expect[n] == '.' {
|
||||||
|
expect = expect[n+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overkill but easy.
|
||||||
|
f.b.Reset()
|
||||||
|
printer.Fprint(&f.b, f.fset, actual)
|
||||||
|
return f.b.String() == expect
|
||||||
|
}
|
351
cmd/vet/print.go
Normal file
351
cmd/vet/print.go
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
// Copyright 2010 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 contains the printf-checker.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
var printfuncs = flag.String("printfuncs", "", "comma-separated list of print function names to check")
|
||||||
|
|
||||||
|
// printfList records the formatted-print functions. The value is the location
|
||||||
|
// of the format parameter. Names are lower-cased so the lookup is
|
||||||
|
// case insensitive.
|
||||||
|
var printfList = map[string]int{
|
||||||
|
"errorf": 0,
|
||||||
|
"fatalf": 0,
|
||||||
|
"fprintf": 1,
|
||||||
|
"panicf": 0,
|
||||||
|
"printf": 0,
|
||||||
|
"sprintf": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// printList records the unformatted-print functions. The value is the location
|
||||||
|
// of the first parameter to be printed. Names are lower-cased so the lookup is
|
||||||
|
// case insensitive.
|
||||||
|
var printList = map[string]int{
|
||||||
|
"error": 0,
|
||||||
|
"fatal": 0,
|
||||||
|
"fprint": 1, "fprintln": 1,
|
||||||
|
"panic": 0, "panicln": 0,
|
||||||
|
"print": 0, "println": 0,
|
||||||
|
"sprint": 0, "sprintln": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkCall triggers the print-specific checks if the call invokes a print function.
|
||||||
|
func (f *File) checkFmtPrintfCall(call *ast.CallExpr, Name string) {
|
||||||
|
if !vet("printf") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name := strings.ToLower(Name)
|
||||||
|
if skip, ok := printfList[name]; ok {
|
||||||
|
f.checkPrintf(call, Name, skip)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if skip, ok := printList[name]; ok {
|
||||||
|
f.checkPrint(call, Name, skip)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// literal returns the literal value represented by the expression, or nil if it is not a literal.
|
||||||
|
func (f *File) literal(value ast.Expr) *ast.BasicLit {
|
||||||
|
switch v := value.(type) {
|
||||||
|
case *ast.BasicLit:
|
||||||
|
return v
|
||||||
|
case *ast.ParenExpr:
|
||||||
|
return f.literal(v.X)
|
||||||
|
case *ast.BinaryExpr:
|
||||||
|
if v.Op != token.ADD {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
litX := f.literal(v.X)
|
||||||
|
litY := f.literal(v.Y)
|
||||||
|
if litX != nil && litY != nil {
|
||||||
|
lit := *litX
|
||||||
|
x, errX := strconv.Unquote(litX.Value)
|
||||||
|
y, errY := strconv.Unquote(litY.Value)
|
||||||
|
if errX == nil && errY == nil {
|
||||||
|
return &ast.BasicLit{
|
||||||
|
ValuePos: lit.ValuePos,
|
||||||
|
Kind: lit.Kind,
|
||||||
|
Value: strconv.Quote(x + y),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ast.Ident:
|
||||||
|
// See if it's a constant or initial value (we can't tell the difference).
|
||||||
|
if v.Obj == nil || v.Obj.Decl == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
valueSpec, ok := v.Obj.Decl.(*ast.ValueSpec)
|
||||||
|
if ok && len(valueSpec.Names) == len(valueSpec.Values) {
|
||||||
|
// Find the index in the list of names
|
||||||
|
var i int
|
||||||
|
for i = 0; i < len(valueSpec.Names); i++ {
|
||||||
|
if valueSpec.Names[i].Name == v.Name {
|
||||||
|
if lit, ok := valueSpec.Values[i].(*ast.BasicLit); ok {
|
||||||
|
return lit
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkPrintf checks a call to a formatted print routine such as Printf.
|
||||||
|
// call.Args[formatIndex] is (well, should be) the format argument.
|
||||||
|
func (f *File) checkPrintf(call *ast.CallExpr, name string, formatIndex int) {
|
||||||
|
if formatIndex >= len(call.Args) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lit := f.literal(call.Args[formatIndex])
|
||||||
|
if lit == nil {
|
||||||
|
if *verbose {
|
||||||
|
f.Warn(call.Pos(), "can't check non-literal format in call to", name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if lit.Kind != token.STRING {
|
||||||
|
f.Badf(call.Pos(), "literal %v not a string in call to", lit.Value, name)
|
||||||
|
}
|
||||||
|
format, err := strconv.Unquote(lit.Value)
|
||||||
|
if err != nil {
|
||||||
|
// Shouldn't happen if parser returned no errors, but be safe.
|
||||||
|
f.Badf(call.Pos(), "invalid quoted string literal")
|
||||||
|
}
|
||||||
|
firstArg := formatIndex + 1 // Arguments are immediately after format string.
|
||||||
|
if !strings.Contains(format, "%") {
|
||||||
|
if len(call.Args) > firstArg {
|
||||||
|
f.Badf(call.Pos(), "no formatting directive in %s call", name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Hard part: check formats against args.
|
||||||
|
argNum := firstArg
|
||||||
|
for i, w := 0, 0; i < len(format); i += w {
|
||||||
|
w = 1
|
||||||
|
if format[i] == '%' {
|
||||||
|
verb, flags, nbytes, nargs := f.parsePrintfVerb(call, format[i:])
|
||||||
|
w = nbytes
|
||||||
|
if verb == '%' { // "%%" does nothing interesting.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// If we've run out of args, print after loop will pick that up.
|
||||||
|
if argNum+nargs <= len(call.Args) {
|
||||||
|
f.checkPrintfArg(call, verb, flags, argNum, nargs)
|
||||||
|
}
|
||||||
|
argNum += nargs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: Dotdotdot is hard.
|
||||||
|
if call.Ellipsis.IsValid() && argNum != len(call.Args) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if argNum != len(call.Args) {
|
||||||
|
expect := argNum - firstArg
|
||||||
|
numArgs := len(call.Args) - firstArg
|
||||||
|
f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsePrintfVerb returns the verb that begins the format string, along with its flags,
|
||||||
|
// the number of bytes to advance the format to step past the verb, and number of
|
||||||
|
// arguments it consumes.
|
||||||
|
func (f *File) parsePrintfVerb(call *ast.CallExpr, format string) (verb rune, flags []byte, nbytes, nargs int) {
|
||||||
|
// There's guaranteed a percent sign.
|
||||||
|
flags = make([]byte, 0, 5)
|
||||||
|
nbytes = 1
|
||||||
|
end := len(format)
|
||||||
|
// There may be flags.
|
||||||
|
FlagLoop:
|
||||||
|
for nbytes < end {
|
||||||
|
switch format[nbytes] {
|
||||||
|
case '#', '0', '+', '-', ' ':
|
||||||
|
flags = append(flags, format[nbytes])
|
||||||
|
nbytes++
|
||||||
|
default:
|
||||||
|
break FlagLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getNum := func() {
|
||||||
|
if nbytes < end && format[nbytes] == '*' {
|
||||||
|
nbytes++
|
||||||
|
nargs++
|
||||||
|
} else {
|
||||||
|
for nbytes < end && '0' <= format[nbytes] && format[nbytes] <= '9' {
|
||||||
|
nbytes++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// There may be a width.
|
||||||
|
getNum()
|
||||||
|
// If there's a period, there may be a precision.
|
||||||
|
if nbytes < end && format[nbytes] == '.' {
|
||||||
|
flags = append(flags, '.') // Treat precision as a flag.
|
||||||
|
nbytes++
|
||||||
|
getNum()
|
||||||
|
}
|
||||||
|
// Now a verb.
|
||||||
|
c, w := utf8.DecodeRuneInString(format[nbytes:])
|
||||||
|
nbytes += w
|
||||||
|
verb = c
|
||||||
|
if c != '%' {
|
||||||
|
nargs++
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask.
|
||||||
|
type printfArgType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
argBool printfArgType = 1 << iota
|
||||||
|
argInt
|
||||||
|
argRune
|
||||||
|
argString
|
||||||
|
argFloat
|
||||||
|
argPointer
|
||||||
|
anyType printfArgType = ^0
|
||||||
|
)
|
||||||
|
|
||||||
|
type printVerb struct {
|
||||||
|
verb rune
|
||||||
|
flags string // known flags are all ASCII
|
||||||
|
typ printfArgType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common flag sets for printf verbs.
|
||||||
|
const (
|
||||||
|
numFlag = " -+.0"
|
||||||
|
sharpNumFlag = " -+.0#"
|
||||||
|
allFlags = " -+.0#"
|
||||||
|
)
|
||||||
|
|
||||||
|
// printVerbs identifies which flags are known to printf for each verb.
|
||||||
|
// TODO: A type that implements Formatter may do what it wants, and vet
|
||||||
|
// will complain incorrectly.
|
||||||
|
var printVerbs = []printVerb{
|
||||||
|
// '-' is a width modifier, always valid.
|
||||||
|
// '.' is a precision for float, max width for strings.
|
||||||
|
// '+' is required sign for numbers, Go format for %v.
|
||||||
|
// '#' is alternate format for several verbs.
|
||||||
|
// ' ' is spacer for numbers
|
||||||
|
{'b', numFlag, argInt | argFloat},
|
||||||
|
{'c', "-", argRune | argInt},
|
||||||
|
{'d', numFlag, argInt},
|
||||||
|
{'e', numFlag, argFloat},
|
||||||
|
{'E', numFlag, argFloat},
|
||||||
|
{'f', numFlag, argFloat},
|
||||||
|
{'F', numFlag, argFloat},
|
||||||
|
{'g', numFlag, argFloat},
|
||||||
|
{'G', numFlag, argFloat},
|
||||||
|
{'o', sharpNumFlag, argInt},
|
||||||
|
{'p', "-#", argPointer},
|
||||||
|
{'q', " -+.0#", argRune | argInt | argString},
|
||||||
|
{'s', " -+.0", argString},
|
||||||
|
{'t', "-", argBool},
|
||||||
|
{'T', "-", anyType},
|
||||||
|
{'U', "-#", argRune | argInt},
|
||||||
|
{'v', allFlags, anyType},
|
||||||
|
{'x', sharpNumFlag, argRune | argInt | argString},
|
||||||
|
{'X', sharpNumFlag, argRune | argInt | argString},
|
||||||
|
}
|
||||||
|
|
||||||
|
const printfVerbs = "bcdeEfFgGopqstTvxUX"
|
||||||
|
|
||||||
|
func (f *File) checkPrintfArg(call *ast.CallExpr, verb rune, flags []byte, argNum, nargs int) {
|
||||||
|
// Linear scan is fast enough for a small list.
|
||||||
|
for _, v := range printVerbs {
|
||||||
|
if v.verb == verb {
|
||||||
|
for _, flag := range flags {
|
||||||
|
if !strings.ContainsRune(v.flags, rune(flag)) {
|
||||||
|
f.Badf(call.Pos(), "unrecognized printf flag for verb %q: %q", verb, flag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Verb is good. If nargs>1, we have something like %.*s and all but the final
|
||||||
|
// arg must be integer.
|
||||||
|
for i := 0; i < nargs-1; i++ {
|
||||||
|
if !f.matchArgType(argInt, call.Args[argNum+i]) {
|
||||||
|
f.Badf(call.Pos(), "arg %s for * in printf format not of type int", f.gofmt(call.Args[argNum+i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, v := range printVerbs {
|
||||||
|
if v.verb == verb {
|
||||||
|
arg := call.Args[argNum+nargs-1]
|
||||||
|
if !f.matchArgType(v.typ, arg) {
|
||||||
|
typeString := ""
|
||||||
|
if typ := f.pkg.types[arg]; typ != nil {
|
||||||
|
typeString = typ.String()
|
||||||
|
}
|
||||||
|
f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), verb, typeString)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.Badf(call.Pos(), "unrecognized printf verb %q", verb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkPrint checks a call to an unformatted print routine such as Println.
|
||||||
|
// call.Args[firstArg] is the first argument to be printed.
|
||||||
|
func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) {
|
||||||
|
isLn := strings.HasSuffix(name, "ln")
|
||||||
|
isF := strings.HasPrefix(name, "F")
|
||||||
|
args := call.Args
|
||||||
|
// check for Println(os.Stderr, ...)
|
||||||
|
if firstArg == 0 && !isF && len(args) > 0 {
|
||||||
|
if sel, ok := args[0].(*ast.SelectorExpr); ok {
|
||||||
|
if x, ok := sel.X.(*ast.Ident); ok {
|
||||||
|
if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") {
|
||||||
|
f.Badf(call.Pos(), "first argument to %s is %s.%s", name, x.Name, sel.Sel.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(args) <= firstArg {
|
||||||
|
// If we have a call to a method called Error that satisfies the Error interface,
|
||||||
|
// then it's ok. Otherwise it's something like (*T).Error from the testing package
|
||||||
|
// and we need to check it.
|
||||||
|
if name == "Error" && f.isErrorMethodCall(call) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// If it's an Error call now, it's probably for printing errors.
|
||||||
|
if !isLn {
|
||||||
|
// Check the signature to be sure: there are niladic functions called "error".
|
||||||
|
if firstArg != 0 || f.numArgsInSignature(call) != firstArg {
|
||||||
|
f.Badf(call.Pos(), "no args in %s call", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
arg := args[firstArg]
|
||||||
|
if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
|
||||||
|
if strings.Contains(lit.Value, "%") {
|
||||||
|
f.Badf(call.Pos(), "possible formatting directive in %s call", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isLn {
|
||||||
|
// The last item, if a string, should not have a newline.
|
||||||
|
arg = args[len(call.Args)-1]
|
||||||
|
if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
|
||||||
|
if strings.HasSuffix(lit.Value, `\n"`) {
|
||||||
|
f.Badf(call.Pos(), "%s call ends with newline", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
cmd/vet/rangeloop.go
Normal file
65
cmd/vet/rangeloop.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2012 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 contains the code to check range loop variables bound inside function
|
||||||
|
literals that are deferred or launched in new goroutines. We only check
|
||||||
|
instances where the defer or go statement is the last statement in the loop
|
||||||
|
body, as otherwise we would need whole program analysis.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
for i, v := range s {
|
||||||
|
go func() {
|
||||||
|
println(i, v) // not what you might expect
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
See: http://golang.org/doc/go_faq.html#closures_and_goroutines
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "go/ast"
|
||||||
|
|
||||||
|
// checkRangeLoop walks the body of the provided range statement, checking if
|
||||||
|
// its index or value variables are used unsafely inside goroutines or deferred
|
||||||
|
// function literals.
|
||||||
|
func checkRangeLoop(f *File, n *ast.RangeStmt) {
|
||||||
|
if !vet("rangeloops") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
key, _ := n.Key.(*ast.Ident)
|
||||||
|
val, _ := n.Value.(*ast.Ident)
|
||||||
|
if key == nil && val == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sl := n.Body.List
|
||||||
|
if len(sl) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var last *ast.CallExpr
|
||||||
|
switch s := sl[len(sl)-1].(type) {
|
||||||
|
case *ast.GoStmt:
|
||||||
|
last = s.Call
|
||||||
|
case *ast.DeferStmt:
|
||||||
|
last = s.Call
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lit, ok := last.Fun.(*ast.FuncLit)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ast.Inspect(lit.Body, func(n ast.Node) bool {
|
||||||
|
id, ok := n.(*ast.Ident)
|
||||||
|
if !ok || id.Obj == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if key != nil && id.Obj == key.Obj || val != nil && id.Obj == val.Obj {
|
||||||
|
f.Warn(id.Pos(), "range variable", id.Name, "enclosed by function")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
37
cmd/vet/structtag.go
Normal file
37
cmd/vet/structtag.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2010 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 contains the test for canonical struct tags.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// checkField checks a struct field tag.
|
||||||
|
func (f *File) checkCanonicalFieldTag(field *ast.Field) {
|
||||||
|
if !vet("structtags") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if field.Tag == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tag, err := strconv.Unquote(field.Tag.Value)
|
||||||
|
if err != nil {
|
||||||
|
f.Badf(field.Pos(), "unable to read struct tag %s", field.Tag.Value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check tag for validity by appending
|
||||||
|
// new key:value to end and checking that
|
||||||
|
// the tag parsing code can find it.
|
||||||
|
if reflect.StructTag(tag+` _gofix:"_magic"`).Get("_gofix") != "_magic" {
|
||||||
|
f.Badf(field.Pos(), "struct field tag %s not compatible with reflect.StructTag.Get", field.Tag.Value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
164
cmd/vet/taglit.go
Normal file
164
cmd/vet/taglit.go
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
// Copyright 2012 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 contains the test for untagged struct literals.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"go/ast"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only")
|
||||||
|
|
||||||
|
// checkUntaggedLiteral checks if a composite literal is a struct literal with
|
||||||
|
// untagged fields.
|
||||||
|
func (f *File) checkUntaggedLiteral(c *ast.CompositeLit) {
|
||||||
|
if !vet("composites") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := c.Type
|
||||||
|
for {
|
||||||
|
if typ1, ok := c.Type.(*ast.ParenExpr); ok {
|
||||||
|
typ = typ1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typ.(type) {
|
||||||
|
case *ast.ArrayType:
|
||||||
|
return
|
||||||
|
case *ast.MapType:
|
||||||
|
return
|
||||||
|
case *ast.StructType:
|
||||||
|
return // a literal struct type does not need to use tags
|
||||||
|
case *ast.Ident:
|
||||||
|
// A simple type name like t or T does not need tags either,
|
||||||
|
// since it is almost certainly declared in the current package.
|
||||||
|
// (The exception is names being used via import . "pkg", but
|
||||||
|
// those are already breaking the Go 1 compatibility promise,
|
||||||
|
// so not reporting potential additional breakage seems okay.)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise the type is a selector like pkg.Name.
|
||||||
|
// We only care if pkg.Name is a struct, not if it's a map, array, or slice.
|
||||||
|
isStruct, typeString := f.pkg.isStruct(c)
|
||||||
|
if !isStruct {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if typeString == "" { // isStruct doesn't know
|
||||||
|
typeString = f.gofmt(typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's a struct, or we can't tell it's not a struct because we don't have types.
|
||||||
|
|
||||||
|
// Check if the CompositeLit contains an untagged field.
|
||||||
|
allKeyValue := true
|
||||||
|
for _, e := range c.Elts {
|
||||||
|
if _, ok := e.(*ast.KeyValueExpr); !ok {
|
||||||
|
allKeyValue = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if allKeyValue {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the CompositeLit's type has the form pkg.Typ.
|
||||||
|
s, ok := c.Type.(*ast.SelectorExpr)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pkg, ok := s.X.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the package name to an import path, and compare to a whitelist.
|
||||||
|
path := pkgPath(f, pkg.Name)
|
||||||
|
if path == "" {
|
||||||
|
f.Badf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
typeName := path + "." + s.Sel.Name
|
||||||
|
if *compositeWhiteList && untaggedLiteralWhitelist[typeName] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Warn(c.Pos(), typeString+" composite literal uses untagged fields")
|
||||||
|
}
|
||||||
|
|
||||||
|
// pkgPath returns the import path "image/png" for the package name "png".
|
||||||
|
//
|
||||||
|
// This is based purely on syntax and convention, and not on the imported
|
||||||
|
// package's contents. It will be incorrect if a package name differs from the
|
||||||
|
// leaf element of the import path, or if the package was a dot import.
|
||||||
|
func pkgPath(f *File, pkgName string) (path string) {
|
||||||
|
for _, x := range f.file.Imports {
|
||||||
|
s := strings.Trim(x.Path.Value, `"`)
|
||||||
|
if x.Name != nil {
|
||||||
|
// Catch `import pkgName "foo/bar"`.
|
||||||
|
if x.Name.Name == pkgName {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Catch `import "pkgName"` or `import "foo/bar/pkgName"`.
|
||||||
|
if s == pkgName || strings.HasSuffix(s, "/"+pkgName) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var untaggedLiteralWhitelist = map[string]bool{
|
||||||
|
/*
|
||||||
|
These types are actually slices. Syntactically, we cannot tell
|
||||||
|
whether the Typ in pkg.Typ{1, 2, 3} is a slice or a struct, so we
|
||||||
|
whitelist all the standard package library's exported slice types.
|
||||||
|
|
||||||
|
find $GOROOT/src/pkg -type f | grep -v _test.go | xargs grep '^type.*\[\]' | \
|
||||||
|
grep -v ' map\[' | sed 's,/[^/]*go.type,,' | sed 's,.*src/pkg/,,' | \
|
||||||
|
sed 's, ,.,' | sed 's, .*,,' | grep -v '\.[a-z]' | \
|
||||||
|
sort | awk '{ print "\"" $0 "\": true," }'
|
||||||
|
*/
|
||||||
|
"crypto/x509/pkix.RDNSequence": true,
|
||||||
|
"crypto/x509/pkix.RelativeDistinguishedNameSET": true,
|
||||||
|
"database/sql.RawBytes": true,
|
||||||
|
"debug/macho.LoadBytes": true,
|
||||||
|
"encoding/asn1.ObjectIdentifier": true,
|
||||||
|
"encoding/asn1.RawContent": true,
|
||||||
|
"encoding/json.RawMessage": true,
|
||||||
|
"encoding/xml.CharData": true,
|
||||||
|
"encoding/xml.Comment": true,
|
||||||
|
"encoding/xml.Directive": true,
|
||||||
|
"go/scanner.ErrorList": true,
|
||||||
|
"image/color.Palette": true,
|
||||||
|
"net.HardwareAddr": true,
|
||||||
|
"net.IP": true,
|
||||||
|
"net.IPMask": true,
|
||||||
|
"sort.Float64Slice": true,
|
||||||
|
"sort.IntSlice": true,
|
||||||
|
"sort.StringSlice": true,
|
||||||
|
"unicode.SpecialCase": true,
|
||||||
|
|
||||||
|
// These image and image/color struct types are frozen. We will never add fields to them.
|
||||||
|
"image/color.Alpha16": true,
|
||||||
|
"image/color.Alpha": true,
|
||||||
|
"image/color.Gray16": true,
|
||||||
|
"image/color.Gray": true,
|
||||||
|
"image/color.NRGBA64": true,
|
||||||
|
"image/color.NRGBA": true,
|
||||||
|
"image/color.RGBA64": true,
|
||||||
|
"image/color.RGBA": true,
|
||||||
|
"image/color.YCbCr": true,
|
||||||
|
"image.Point": true,
|
||||||
|
"image.Rectangle": true,
|
||||||
|
}
|
24
cmd/vet/test_asm.go
Normal file
24
cmd/vet/test_asm.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright 2010 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.
|
||||||
|
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
// This file contains declarations to test the assembly in test_asm.s.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func arg1(x int8, y uint8)
|
||||||
|
func arg2(x int16, y uint16)
|
||||||
|
func arg4(x int32, y uint32)
|
||||||
|
func arg8(x int64, y uint64)
|
||||||
|
func argint(x int, y uint)
|
||||||
|
func argptr(x *byte, y *byte, c chan int, m map[int]int, f func())
|
||||||
|
func argstring(x, y string)
|
||||||
|
func argslice(x, y []string)
|
||||||
|
func argiface(x interface{}, y interface {
|
||||||
|
m()
|
||||||
|
})
|
||||||
|
func returnint() int
|
||||||
|
func returnbyte(x int) byte
|
||||||
|
func returnnamed(x byte) (r1 int, r2 int16, r3 string, r4 byte)
|
247
cmd/vet/test_asm1.s
Normal file
247
cmd/vet/test_asm1.s
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
// +build amd64
|
||||||
|
// +build vet_test
|
||||||
|
|
||||||
|
TEXT ·arg1(SB),0,$0-2
|
||||||
|
MOVB x+0(FP), AX
|
||||||
|
MOVB y+1(FP), BX
|
||||||
|
MOVW x+0(FP), AX // ERROR "\[amd64\] invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
|
||||||
|
MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value"
|
||||||
|
MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int8 is 1-byte value"
|
||||||
|
MOVL y+1(FP), AX // ERROR "invalid MOVL of y\+1\(FP\); uint8 is 1-byte value"
|
||||||
|
MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int8 is 1-byte value"
|
||||||
|
MOVQ y+1(FP), AX // ERROR "invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value"
|
||||||
|
MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
|
||||||
|
TESTB x+0(FP), AX
|
||||||
|
TESTB y+1(FP), BX
|
||||||
|
TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int8 is 1-byte value"
|
||||||
|
TESTW y+1(FP), AX // ERROR "invalid TESTW of y\+1\(FP\); uint8 is 1-byte value"
|
||||||
|
TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int8 is 1-byte value"
|
||||||
|
TESTL y+1(FP), AX // ERROR "invalid TESTL of y\+1\(FP\); uint8 is 1-byte value"
|
||||||
|
TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int8 is 1-byte value"
|
||||||
|
TESTQ y+1(FP), AX // ERROR "invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value"
|
||||||
|
TESTB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
|
||||||
|
TESTB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·arg2(SB),0,$0-4
|
||||||
|
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int16 is 2-byte value"
|
||||||
|
MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
|
||||||
|
MOVW x+0(FP), AX
|
||||||
|
MOVW y+2(FP), BX
|
||||||
|
MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int16 is 2-byte value"
|
||||||
|
MOVL y+2(FP), AX // ERROR "invalid MOVL of y\+2\(FP\); uint16 is 2-byte value"
|
||||||
|
MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int16 is 2-byte value"
|
||||||
|
MOVQ y+2(FP), AX // ERROR "invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value"
|
||||||
|
MOVW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
|
||||||
|
TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int16 is 2-byte value"
|
||||||
|
TESTB y+2(FP), AX // ERROR "invalid TESTB of y\+2\(FP\); uint16 is 2-byte value"
|
||||||
|
TESTW x+0(FP), AX
|
||||||
|
TESTW y+2(FP), BX
|
||||||
|
TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int16 is 2-byte value"
|
||||||
|
TESTL y+2(FP), AX // ERROR "invalid TESTL of y\+2\(FP\); uint16 is 2-byte value"
|
||||||
|
TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int16 is 2-byte value"
|
||||||
|
TESTQ y+2(FP), AX // ERROR "invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value"
|
||||||
|
TESTW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
|
||||||
|
TESTW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·arg4(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
|
||||||
|
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
|
||||||
|
MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int32 is 4-byte value"
|
||||||
|
MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint32 is 4-byte value"
|
||||||
|
MOVL x+0(FP), AX
|
||||||
|
MOVL y+4(FP), AX
|
||||||
|
MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int32 is 4-byte value"
|
||||||
|
MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value"
|
||||||
|
MOVL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
|
||||||
|
TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int32 is 4-byte value"
|
||||||
|
TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint32 is 4-byte value"
|
||||||
|
TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int32 is 4-byte value"
|
||||||
|
TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint32 is 4-byte value"
|
||||||
|
TESTL x+0(FP), AX
|
||||||
|
TESTL y+4(FP), AX
|
||||||
|
TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int32 is 4-byte value"
|
||||||
|
TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value"
|
||||||
|
TESTL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
|
||||||
|
TESTL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
|
||||||
|
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
|
||||||
|
MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value"
|
||||||
|
MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value"
|
||||||
|
MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int64 is 8-byte value"
|
||||||
|
MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint64 is 8-byte value"
|
||||||
|
MOVQ x+0(FP), AX
|
||||||
|
MOVQ y+8(FP), AX
|
||||||
|
MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
|
||||||
|
TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int64 is 8-byte value"
|
||||||
|
TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint64 is 8-byte value"
|
||||||
|
TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int64 is 8-byte value"
|
||||||
|
TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint64 is 8-byte value"
|
||||||
|
TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int64 is 8-byte value"
|
||||||
|
TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint64 is 8-byte value"
|
||||||
|
TESTQ x+0(FP), AX
|
||||||
|
TESTQ y+8(FP), AX
|
||||||
|
TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
|
||||||
|
TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
|
||||||
|
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 8-byte value"
|
||||||
|
MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint is 8-byte value"
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int is 8-byte value"
|
||||||
|
MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint is 8-byte value"
|
||||||
|
MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int is 8-byte value"
|
||||||
|
MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint is 8-byte value"
|
||||||
|
MOVQ x+0(FP), AX
|
||||||
|
MOVQ y+8(FP), AX
|
||||||
|
MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
|
||||||
|
TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int is 8-byte value"
|
||||||
|
TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint is 8-byte value"
|
||||||
|
TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int is 8-byte value"
|
||||||
|
TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint is 8-byte value"
|
||||||
|
TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int is 8-byte value"
|
||||||
|
TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint is 8-byte value"
|
||||||
|
TESTQ x+0(FP), AX
|
||||||
|
TESTQ y+8(FP), AX
|
||||||
|
TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
|
||||||
|
TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-40"
|
||||||
|
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 8-byte value"
|
||||||
|
MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); \*byte is 8-byte value"
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 8-byte value"
|
||||||
|
MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); \*byte is 8-byte value"
|
||||||
|
MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); \*byte is 8-byte value"
|
||||||
|
MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); \*byte is 8-byte value"
|
||||||
|
MOVQ x+0(FP), AX
|
||||||
|
MOVQ y+8(FP), AX
|
||||||
|
MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
|
||||||
|
TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); \*byte is 8-byte value"
|
||||||
|
TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); \*byte is 8-byte value"
|
||||||
|
TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); \*byte is 8-byte value"
|
||||||
|
TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); \*byte is 8-byte value"
|
||||||
|
TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); \*byte is 8-byte value"
|
||||||
|
TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); \*byte is 8-byte value"
|
||||||
|
TESTQ x+0(FP), AX
|
||||||
|
TESTQ y+8(FP), AX
|
||||||
|
TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
|
||||||
|
TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
|
||||||
|
MOVL c+16(FP), AX // ERROR "invalid MOVL of c\+16\(FP\); chan int is 8-byte value"
|
||||||
|
MOVL m+24(FP), AX // ERROR "invalid MOVL of m\+24\(FP\); map\[int\]int is 8-byte value"
|
||||||
|
MOVL f+32(FP), AX // ERROR "invalid MOVL of f\+32\(FP\); func\(\) is 8-byte value"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·argstring(SB),0,$32 // ERROR "wrong argument size 0; expected \$\.\.\.-32"
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); string base is 8-byte value"
|
||||||
|
MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); string base is 8-byte value"
|
||||||
|
MOVQ x+0(FP), AX
|
||||||
|
MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 8-byte value"
|
||||||
|
MOVL x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); string base is 8-byte value"
|
||||||
|
MOVQ x_base+0(FP), AX
|
||||||
|
MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
|
||||||
|
MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
|
||||||
|
MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
|
||||||
|
MOVW x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); string len is 8-byte value"
|
||||||
|
MOVL x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); string len is 8-byte value"
|
||||||
|
MOVQ x_len+8(FP), AX
|
||||||
|
MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+16\(FP\)"
|
||||||
|
MOVQ y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·argslice(SB),0,$48 // ERROR "wrong argument size 0; expected \$\.\.\.-48"
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); slice base is 8-byte value"
|
||||||
|
MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); slice base is 8-byte value"
|
||||||
|
MOVQ x+0(FP), AX
|
||||||
|
MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value"
|
||||||
|
MOVL x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); slice base is 8-byte value"
|
||||||
|
MOVQ x_base+0(FP), AX
|
||||||
|
MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
|
||||||
|
MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
|
||||||
|
MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
|
||||||
|
MOVW x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value"
|
||||||
|
MOVL x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); slice len is 8-byte value"
|
||||||
|
MOVQ x_len+8(FP), AX
|
||||||
|
MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
|
||||||
|
MOVL x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
|
||||||
|
MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
|
||||||
|
MOVW x_cap+16(FP), AX // ERROR "invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value"
|
||||||
|
MOVL x_cap+16(FP), AX // ERROR "invalid MOVL of x_cap\+16\(FP\); slice cap is 8-byte value"
|
||||||
|
MOVQ x_cap+16(FP), AX
|
||||||
|
MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+24\(FP\)"
|
||||||
|
MOVQ y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)"
|
||||||
|
MOVQ y_cap+16(FP), AX // ERROR "invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·argiface(SB),0,$0-32
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); interface type is 8-byte value"
|
||||||
|
MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); interface type is 8-byte value"
|
||||||
|
MOVQ x+0(FP), AX
|
||||||
|
MOVW x_type+0(FP), AX // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value"
|
||||||
|
MOVL x_type+0(FP), AX // ERROR "invalid MOVL of x_type\+0\(FP\); interface type is 8-byte value"
|
||||||
|
MOVQ x_type+0(FP), AX
|
||||||
|
MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
|
||||||
|
MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
|
||||||
|
MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
|
||||||
|
MOVL x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
|
||||||
|
MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
|
||||||
|
MOVW x_data+8(FP), AX // ERROR "invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value"
|
||||||
|
MOVL x_data+8(FP), AX // ERROR "invalid MOVL of x_data\+8\(FP\); interface data is 8-byte value"
|
||||||
|
MOVQ x_data+8(FP), AX
|
||||||
|
MOVW y+16(FP), AX // ERROR "invalid MOVW of y\+16\(FP\); interface itable is 8-byte value"
|
||||||
|
MOVL y+16(FP), AX // ERROR "invalid MOVL of y\+16\(FP\); interface itable is 8-byte value"
|
||||||
|
MOVQ y+16(FP), AX
|
||||||
|
MOVW y_itable+16(FP), AX // ERROR "invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value"
|
||||||
|
MOVL y_itable+16(FP), AX // ERROR "invalid MOVL of y_itable\+16\(FP\); interface itable is 8-byte value"
|
||||||
|
MOVQ y_itable+16(FP), AX
|
||||||
|
MOVQ y_type+16(FP), AX // ERROR "unknown variable y_type; offset 16 is y_itable\+16\(FP\)"
|
||||||
|
MOVW y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
|
||||||
|
MOVL y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
|
||||||
|
MOVQ y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
|
||||||
|
MOVW y_data+24(FP), AX // ERROR "invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value"
|
||||||
|
MOVL y_data+24(FP), AX // ERROR "invalid MOVL of y_data\+24\(FP\); interface data is 8-byte value"
|
||||||
|
MOVQ y_data+24(FP), AX
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·returnint(SB),0,$0-8
|
||||||
|
MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 8-byte value"
|
||||||
|
MOVW AX, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 8-byte value"
|
||||||
|
MOVL AX, ret+0(FP) // ERROR "invalid MOVL of ret\+0\(FP\); int is 8-byte value"
|
||||||
|
MOVQ AX, ret+0(FP)
|
||||||
|
MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
|
||||||
|
MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·returnbyte(SB),0,$0-9
|
||||||
|
MOVQ x+0(FP), AX
|
||||||
|
MOVB AX, ret+8(FP)
|
||||||
|
MOVW AX, ret+8(FP) // ERROR "invalid MOVW of ret\+8\(FP\); byte is 1-byte value"
|
||||||
|
MOVL AX, ret+8(FP) // ERROR "invalid MOVL of ret\+8\(FP\); byte is 1-byte value"
|
||||||
|
MOVQ AX, ret+8(FP) // ERROR "invalid MOVQ of ret\+8\(FP\); byte is 1-byte value"
|
||||||
|
MOVB AX, ret+7(FP) // ERROR "invalid offset ret\+7\(FP\); expected ret\+8\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·returnnamed(SB),0,$0-41
|
||||||
|
MOVB x+0(FP), AX
|
||||||
|
MOVQ AX, r1+8(FP)
|
||||||
|
MOVW AX, r2+16(FP)
|
||||||
|
MOVQ AX, r3+24(FP)
|
||||||
|
MOVQ AX, r3_base+24(FP)
|
||||||
|
MOVQ AX, r3_len+32(FP)
|
||||||
|
MOVB AX, r4+40(FP)
|
||||||
|
MOVL AX, r1+8(FP) // ERROR "invalid MOVL of r1\+8\(FP\); int is 8-byte value"
|
||||||
|
RET
|
251
cmd/vet/test_asm2.s
Normal file
251
cmd/vet/test_asm2.s
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
// +build 386
|
||||||
|
// +build vet_test
|
||||||
|
|
||||||
|
TEXT ·arg1(SB),0,$0-2
|
||||||
|
MOVB x+0(FP), AX
|
||||||
|
MOVB y+1(FP), BX
|
||||||
|
MOVW x+0(FP), AX // ERROR "\[386\] invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
|
||||||
|
MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value"
|
||||||
|
MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int8 is 1-byte value"
|
||||||
|
MOVL y+1(FP), AX // ERROR "invalid MOVL of y\+1\(FP\); uint8 is 1-byte value"
|
||||||
|
MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int8 is 1-byte value"
|
||||||
|
MOVQ y+1(FP), AX // ERROR "invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value"
|
||||||
|
MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
|
||||||
|
TESTB x+0(FP), AX
|
||||||
|
TESTB y+1(FP), BX
|
||||||
|
TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int8 is 1-byte value"
|
||||||
|
TESTW y+1(FP), AX // ERROR "invalid TESTW of y\+1\(FP\); uint8 is 1-byte value"
|
||||||
|
TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int8 is 1-byte value"
|
||||||
|
TESTL y+1(FP), AX // ERROR "invalid TESTL of y\+1\(FP\); uint8 is 1-byte value"
|
||||||
|
TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int8 is 1-byte value"
|
||||||
|
TESTQ y+1(FP), AX // ERROR "invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value"
|
||||||
|
TESTB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
|
||||||
|
TESTB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·arg2(SB),0,$0-4
|
||||||
|
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int16 is 2-byte value"
|
||||||
|
MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
|
||||||
|
MOVW x+0(FP), AX
|
||||||
|
MOVW y+2(FP), BX
|
||||||
|
MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int16 is 2-byte value"
|
||||||
|
MOVL y+2(FP), AX // ERROR "invalid MOVL of y\+2\(FP\); uint16 is 2-byte value"
|
||||||
|
MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int16 is 2-byte value"
|
||||||
|
MOVQ y+2(FP), AX // ERROR "invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value"
|
||||||
|
MOVW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
|
||||||
|
TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int16 is 2-byte value"
|
||||||
|
TESTB y+2(FP), AX // ERROR "invalid TESTB of y\+2\(FP\); uint16 is 2-byte value"
|
||||||
|
TESTW x+0(FP), AX
|
||||||
|
TESTW y+2(FP), BX
|
||||||
|
TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int16 is 2-byte value"
|
||||||
|
TESTL y+2(FP), AX // ERROR "invalid TESTL of y\+2\(FP\); uint16 is 2-byte value"
|
||||||
|
TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int16 is 2-byte value"
|
||||||
|
TESTQ y+2(FP), AX // ERROR "invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value"
|
||||||
|
TESTW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
|
||||||
|
TESTW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·arg4(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
|
||||||
|
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
|
||||||
|
MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int32 is 4-byte value"
|
||||||
|
MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint32 is 4-byte value"
|
||||||
|
MOVL x+0(FP), AX
|
||||||
|
MOVL y+4(FP), AX
|
||||||
|
MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int32 is 4-byte value"
|
||||||
|
MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value"
|
||||||
|
MOVL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
|
||||||
|
TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int32 is 4-byte value"
|
||||||
|
TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint32 is 4-byte value"
|
||||||
|
TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int32 is 4-byte value"
|
||||||
|
TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint32 is 4-byte value"
|
||||||
|
TESTL x+0(FP), AX
|
||||||
|
TESTL y+4(FP), AX
|
||||||
|
TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int32 is 4-byte value"
|
||||||
|
TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value"
|
||||||
|
TESTL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
|
||||||
|
TESTL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
|
||||||
|
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
|
||||||
|
MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value"
|
||||||
|
MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value"
|
||||||
|
MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)"
|
||||||
|
MOVL x_lo+0(FP), AX
|
||||||
|
MOVL x_hi+4(FP), AX
|
||||||
|
MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)"
|
||||||
|
MOVL y_lo+8(FP), AX
|
||||||
|
MOVL y_hi+12(FP), AX
|
||||||
|
MOVQ x+0(FP), AX
|
||||||
|
MOVQ y+8(FP), AX
|
||||||
|
MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
|
||||||
|
TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int64 is 8-byte value"
|
||||||
|
TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint64 is 8-byte value"
|
||||||
|
TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int64 is 8-byte value"
|
||||||
|
TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint64 is 8-byte value"
|
||||||
|
TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)"
|
||||||
|
TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)"
|
||||||
|
TESTQ x+0(FP), AX
|
||||||
|
TESTQ y+8(FP), AX
|
||||||
|
TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
|
||||||
|
TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
|
||||||
|
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 4-byte value"
|
||||||
|
MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint is 4-byte value"
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int is 4-byte value"
|
||||||
|
MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint is 4-byte value"
|
||||||
|
MOVL x+0(FP), AX
|
||||||
|
MOVL y+4(FP), AX
|
||||||
|
MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int is 4-byte value"
|
||||||
|
MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint is 4-byte value"
|
||||||
|
MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
|
||||||
|
TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int is 4-byte value"
|
||||||
|
TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint is 4-byte value"
|
||||||
|
TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int is 4-byte value"
|
||||||
|
TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint is 4-byte value"
|
||||||
|
TESTL x+0(FP), AX
|
||||||
|
TESTL y+4(FP), AX
|
||||||
|
TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int is 4-byte value"
|
||||||
|
TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint is 4-byte value"
|
||||||
|
TESTQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
|
||||||
|
TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-20"
|
||||||
|
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 4-byte value"
|
||||||
|
MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); \*byte is 4-byte value"
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 4-byte value"
|
||||||
|
MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); \*byte is 4-byte value"
|
||||||
|
MOVL x+0(FP), AX
|
||||||
|
MOVL y+4(FP), AX
|
||||||
|
MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); \*byte is 4-byte value"
|
||||||
|
MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); \*byte is 4-byte value"
|
||||||
|
MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
|
||||||
|
TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); \*byte is 4-byte value"
|
||||||
|
TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); \*byte is 4-byte value"
|
||||||
|
TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); \*byte is 4-byte value"
|
||||||
|
TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); \*byte is 4-byte value"
|
||||||
|
TESTL x+0(FP), AX
|
||||||
|
TESTL y+4(FP), AX
|
||||||
|
TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); \*byte is 4-byte value"
|
||||||
|
TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); \*byte is 4-byte value"
|
||||||
|
TESTQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
|
||||||
|
TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
|
||||||
|
MOVW c+8(FP), AX // ERROR "invalid MOVW of c\+8\(FP\); chan int is 4-byte value"
|
||||||
|
MOVW m+12(FP), AX // ERROR "invalid MOVW of m\+12\(FP\); map\[int\]int is 4-byte value"
|
||||||
|
MOVW f+16(FP), AX // ERROR "invalid MOVW of f\+16\(FP\); func\(\) is 4-byte value"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·argstring(SB),0,$16 // ERROR "wrong argument size 0; expected \$\.\.\.-16"
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); string base is 4-byte value"
|
||||||
|
MOVL x+0(FP), AX
|
||||||
|
MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); string base is 4-byte value"
|
||||||
|
MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 4-byte value"
|
||||||
|
MOVL x_base+0(FP), AX
|
||||||
|
MOVQ x_base+0(FP), AX // ERROR "invalid MOVQ of x_base\+0\(FP\); string base is 4-byte value"
|
||||||
|
MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
|
||||||
|
MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
|
||||||
|
MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
|
||||||
|
MOVW x_len+4(FP), AX // ERROR "invalid MOVW of x_len\+4\(FP\); string len is 4-byte value"
|
||||||
|
MOVL x_len+4(FP), AX
|
||||||
|
MOVQ x_len+4(FP), AX // ERROR "invalid MOVQ of x_len\+4\(FP\); string len is 4-byte value"
|
||||||
|
MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+8\(FP\)"
|
||||||
|
MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·argslice(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24"
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); slice base is 4-byte value"
|
||||||
|
MOVL x+0(FP), AX
|
||||||
|
MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); slice base is 4-byte value"
|
||||||
|
MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 4-byte value"
|
||||||
|
MOVL x_base+0(FP), AX
|
||||||
|
MOVQ x_base+0(FP), AX // ERROR "invalid MOVQ of x_base\+0\(FP\); slice base is 4-byte value"
|
||||||
|
MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
|
||||||
|
MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
|
||||||
|
MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
|
||||||
|
MOVW x_len+4(FP), AX // ERROR "invalid MOVW of x_len\+4\(FP\); slice len is 4-byte value"
|
||||||
|
MOVL x_len+4(FP), AX
|
||||||
|
MOVQ x_len+4(FP), AX // ERROR "invalid MOVQ of x_len\+4\(FP\); slice len is 4-byte value"
|
||||||
|
MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
|
||||||
|
MOVL x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
|
||||||
|
MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
|
||||||
|
MOVW x_cap+8(FP), AX // ERROR "invalid MOVW of x_cap\+8\(FP\); slice cap is 4-byte value"
|
||||||
|
MOVL x_cap+8(FP), AX
|
||||||
|
MOVQ x_cap+8(FP), AX // ERROR "invalid MOVQ of x_cap\+8\(FP\); slice cap is 4-byte value"
|
||||||
|
MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+12\(FP\)"
|
||||||
|
MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)"
|
||||||
|
MOVQ y_cap+8(FP), AX // ERROR "invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·argiface(SB),0,$0-16
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); interface type is 4-byte value"
|
||||||
|
MOVL x+0(FP), AX
|
||||||
|
MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); interface type is 4-byte value"
|
||||||
|
MOVW x_type+0(FP), AX // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 4-byte value"
|
||||||
|
MOVL x_type+0(FP), AX
|
||||||
|
MOVQ x_type+0(FP), AX // ERROR "invalid MOVQ of x_type\+0\(FP\); interface type is 4-byte value"
|
||||||
|
MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
|
||||||
|
MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
|
||||||
|
MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
|
||||||
|
MOVL x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
|
||||||
|
MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
|
||||||
|
MOVW x_data+4(FP), AX // ERROR "invalid MOVW of x_data\+4\(FP\); interface data is 4-byte value"
|
||||||
|
MOVL x_data+4(FP), AX
|
||||||
|
MOVQ x_data+4(FP), AX // ERROR "invalid MOVQ of x_data\+4\(FP\); interface data is 4-byte value"
|
||||||
|
MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); interface itable is 4-byte value"
|
||||||
|
MOVL y+8(FP), AX
|
||||||
|
MOVQ y+8(FP), AX // ERROR "invalid MOVQ of y\+8\(FP\); interface itable is 4-byte value"
|
||||||
|
MOVW y_itable+8(FP), AX // ERROR "invalid MOVW of y_itable\+8\(FP\); interface itable is 4-byte value"
|
||||||
|
MOVL y_itable+8(FP), AX
|
||||||
|
MOVQ y_itable+8(FP), AX // ERROR "invalid MOVQ of y_itable\+8\(FP\); interface itable is 4-byte value"
|
||||||
|
MOVQ y_type+8(FP), AX // ERROR "unknown variable y_type; offset 8 is y_itable\+8\(FP\)"
|
||||||
|
MOVW y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
|
||||||
|
MOVL y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
|
||||||
|
MOVQ y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
|
||||||
|
MOVW y_data+12(FP), AX // ERROR "invalid MOVW of y_data\+12\(FP\); interface data is 4-byte value"
|
||||||
|
MOVL y_data+12(FP), AX
|
||||||
|
MOVQ y_data+12(FP), AX // ERROR "invalid MOVQ of y_data\+12\(FP\); interface data is 4-byte value"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·returnint(SB),0,$0-4
|
||||||
|
MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 4-byte value"
|
||||||
|
MOVW AX, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 4-byte value"
|
||||||
|
MOVL AX, ret+0(FP)
|
||||||
|
MOVQ AX, ret+0(FP) // ERROR "invalid MOVQ of ret\+0\(FP\); int is 4-byte value"
|
||||||
|
MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
|
||||||
|
MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·returnbyte(SB),0,$0-5
|
||||||
|
MOVL x+0(FP), AX
|
||||||
|
MOVB AX, ret+4(FP)
|
||||||
|
MOVW AX, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value"
|
||||||
|
MOVL AX, ret+4(FP) // ERROR "invalid MOVL of ret\+4\(FP\); byte is 1-byte value"
|
||||||
|
MOVQ AX, ret+4(FP) // ERROR "invalid MOVQ of ret\+4\(FP\); byte is 1-byte value"
|
||||||
|
MOVB AX, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·returnnamed(SB),0,$0-21
|
||||||
|
MOVB x+0(FP), AX
|
||||||
|
MOVL AX, r1+4(FP)
|
||||||
|
MOVW AX, r2+8(FP)
|
||||||
|
MOVL AX, r3+12(FP)
|
||||||
|
MOVL AX, r3_base+12(FP)
|
||||||
|
MOVL AX, r3_len+16(FP)
|
||||||
|
MOVB AX, r4+20(FP)
|
||||||
|
MOVQ AX, r1+4(FP) // ERROR "invalid MOVQ of r1\+4\(FP\); int is 4-byte value"
|
||||||
|
RET
|
166
cmd/vet/test_asm3.s
Normal file
166
cmd/vet/test_asm3.s
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
// +build arm
|
||||||
|
// +build vet_test
|
||||||
|
|
||||||
|
TEXT ·arg1(SB),0,$0-2
|
||||||
|
MOVB x+0(FP), AX
|
||||||
|
MOVB y+1(FP), BX
|
||||||
|
MOVH x+0(FP), AX // ERROR "\[arm\] invalid MOVH of x\+0\(FP\); int8 is 1-byte value"
|
||||||
|
MOVH y+1(FP), AX // ERROR "invalid MOVH of y\+1\(FP\); uint8 is 1-byte value"
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
|
||||||
|
MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value"
|
||||||
|
MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·arg2(SB),0,$0-4
|
||||||
|
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int16 is 2-byte value"
|
||||||
|
MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
|
||||||
|
MOVH x+0(FP), AX
|
||||||
|
MOVH y+2(FP), BX
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int16 is 2-byte value"
|
||||||
|
MOVW y+2(FP), AX // ERROR "invalid MOVW of y\+2\(FP\); uint16 is 2-byte value"
|
||||||
|
MOVH x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVH y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·arg4(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
|
||||||
|
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
|
||||||
|
MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
|
||||||
|
MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int32 is 4-byte value"
|
||||||
|
MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); uint32 is 4-byte value"
|
||||||
|
MOVW x+0(FP), AX
|
||||||
|
MOVW y+4(FP), AX
|
||||||
|
MOVW x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVW y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
|
||||||
|
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
|
||||||
|
MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
|
||||||
|
MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int64 is 8-byte value"
|
||||||
|
MOVH y+8(FP), AX // ERROR "invalid MOVH of y\+8\(FP\); uint64 is 8-byte value"
|
||||||
|
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)"
|
||||||
|
MOVW x_lo+0(FP), AX
|
||||||
|
MOVW x_hi+4(FP), AX
|
||||||
|
MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)"
|
||||||
|
MOVW y_lo+8(FP), AX
|
||||||
|
MOVW y_hi+12(FP), AX
|
||||||
|
MOVQ x+0(FP), AX
|
||||||
|
MOVQ y+8(FP), AX
|
||||||
|
MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
|
||||||
|
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 4-byte value"
|
||||||
|
MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint is 4-byte value"
|
||||||
|
MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int is 4-byte value"
|
||||||
|
MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); uint is 4-byte value"
|
||||||
|
MOVW x+0(FP), AX
|
||||||
|
MOVW y+4(FP), AX
|
||||||
|
MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-20"
|
||||||
|
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 4-byte value"
|
||||||
|
MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); \*byte is 4-byte value"
|
||||||
|
MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); \*byte is 4-byte value"
|
||||||
|
MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); \*byte is 4-byte value"
|
||||||
|
MOVW x+0(FP), AX
|
||||||
|
MOVW y+4(FP), AX
|
||||||
|
MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
|
||||||
|
MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
|
||||||
|
MOVH c+8(FP), AX // ERROR "invalid MOVH of c\+8\(FP\); chan int is 4-byte value"
|
||||||
|
MOVH m+12(FP), AX // ERROR "invalid MOVH of m\+12\(FP\); map\[int\]int is 4-byte value"
|
||||||
|
MOVH f+16(FP), AX // ERROR "invalid MOVH of f\+16\(FP\); func\(\) is 4-byte value"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·argstring(SB),0,$16 // ERROR "wrong argument size 0; expected \$\.\.\.-16"
|
||||||
|
MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); string base is 4-byte value"
|
||||||
|
MOVW x+0(FP), AX
|
||||||
|
MOVH x_base+0(FP), AX // ERROR "invalid MOVH of x_base\+0\(FP\); string base is 4-byte value"
|
||||||
|
MOVW x_base+0(FP), AX
|
||||||
|
MOVH x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
|
||||||
|
MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
|
||||||
|
MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
|
||||||
|
MOVH x_len+4(FP), AX // ERROR "invalid MOVH of x_len\+4\(FP\); string len is 4-byte value"
|
||||||
|
MOVW x_len+4(FP), AX
|
||||||
|
MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+8\(FP\)"
|
||||||
|
MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·argslice(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24"
|
||||||
|
MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); slice base is 4-byte value"
|
||||||
|
MOVW x+0(FP), AX
|
||||||
|
MOVH x_base+0(FP), AX // ERROR "invalid MOVH of x_base\+0\(FP\); slice base is 4-byte value"
|
||||||
|
MOVW x_base+0(FP), AX
|
||||||
|
MOVH x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
|
||||||
|
MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
|
||||||
|
MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
|
||||||
|
MOVH x_len+4(FP), AX // ERROR "invalid MOVH of x_len\+4\(FP\); slice len is 4-byte value"
|
||||||
|
MOVW x_len+4(FP), AX
|
||||||
|
MOVH x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
|
||||||
|
MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
|
||||||
|
MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
|
||||||
|
MOVH x_cap+8(FP), AX // ERROR "invalid MOVH of x_cap\+8\(FP\); slice cap is 4-byte value"
|
||||||
|
MOVW x_cap+8(FP), AX
|
||||||
|
MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+12\(FP\)"
|
||||||
|
MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)"
|
||||||
|
MOVQ y_cap+8(FP), AX // ERROR "invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·argiface(SB),0,$0-16
|
||||||
|
MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); interface type is 4-byte value"
|
||||||
|
MOVW x+0(FP), AX
|
||||||
|
MOVH x_type+0(FP), AX // ERROR "invalid MOVH of x_type\+0\(FP\); interface type is 4-byte value"
|
||||||
|
MOVW x_type+0(FP), AX
|
||||||
|
MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
|
||||||
|
MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
|
||||||
|
MOVH x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
|
||||||
|
MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
|
||||||
|
MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
|
||||||
|
MOVH x_data+4(FP), AX // ERROR "invalid MOVH of x_data\+4\(FP\); interface data is 4-byte value"
|
||||||
|
MOVW x_data+4(FP), AX
|
||||||
|
MOVH y+8(FP), AX // ERROR "invalid MOVH of y\+8\(FP\); interface itable is 4-byte value"
|
||||||
|
MOVW y+8(FP), AX
|
||||||
|
MOVH y_itable+8(FP), AX // ERROR "invalid MOVH of y_itable\+8\(FP\); interface itable is 4-byte value"
|
||||||
|
MOVW y_itable+8(FP), AX
|
||||||
|
MOVQ y_type+8(FP), AX // ERROR "unknown variable y_type; offset 8 is y_itable\+8\(FP\)"
|
||||||
|
MOVH y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
|
||||||
|
MOVW y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
|
||||||
|
MOVQ y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
|
||||||
|
MOVH y_data+12(FP), AX // ERROR "invalid MOVH of y_data\+12\(FP\); interface data is 4-byte value"
|
||||||
|
MOVW y_data+12(FP), AX
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·returnint(SB),0,$0-4
|
||||||
|
MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 4-byte value"
|
||||||
|
MOVH AX, ret+0(FP) // ERROR "invalid MOVH of ret\+0\(FP\); int is 4-byte value"
|
||||||
|
MOVW AX, ret+0(FP)
|
||||||
|
MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
|
||||||
|
MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·returnbyte(SB),0,$0-5
|
||||||
|
MOVW x+0(FP), AX
|
||||||
|
MOVB AX, ret+4(FP)
|
||||||
|
MOVH AX, ret+4(FP) // ERROR "invalid MOVH of ret\+4\(FP\); byte is 1-byte value"
|
||||||
|
MOVW AX, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value"
|
||||||
|
MOVB AX, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)"
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·returnnamed(SB),0,$0-21
|
||||||
|
MOVB x+0(FP), AX
|
||||||
|
MOVW AX, r1+4(FP)
|
||||||
|
MOVH AX, r2+8(FP)
|
||||||
|
MOVW AX, r3+12(FP)
|
||||||
|
MOVW AX, r3_base+12(FP)
|
||||||
|
MOVW AX, r3_len+16(FP)
|
||||||
|
MOVB AX, r4+20(FP)
|
||||||
|
MOVB AX, r1+4(FP) // ERROR "invalid MOVB of r1\+4\(FP\); int is 4-byte value"
|
||||||
|
RET
|
20
cmd/vet/test_assign.go
Normal file
20
cmd/vet/test_assign.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2013 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 contains tests for the useless-assignment checker.
|
||||||
|
|
||||||
|
// +build vet_test
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
type ST struct {
|
||||||
|
x int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ST) SetX(x int) {
|
||||||
|
// Accidental self-assignment; it should be "s.x = x"
|
||||||
|
x = x // ERROR "self-assignment of x to x"
|
||||||
|
// Another mistake
|
||||||
|
s.x = s.x // ERROR "self-assignment of s.x to s.x"
|
||||||
|
}
|
43
cmd/vet/test_atomic.go
Normal file
43
cmd/vet/test_atomic.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
// +build vet_test
|
||||||
|
|
||||||
|
// This file contains tests for the atomic checker.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Counter uint64
|
||||||
|
|
||||||
|
func AtomicTests() {
|
||||||
|
x := uint64(1)
|
||||||
|
x = atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value"
|
||||||
|
_, x = 10, atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value"
|
||||||
|
x, _ = atomic.AddUint64(&x, 1), 10 // ERROR "direct assignment to atomic value"
|
||||||
|
|
||||||
|
y := &x
|
||||||
|
*y = atomic.AddUint64(y, 1) // ERROR "direct assignment to atomic value"
|
||||||
|
|
||||||
|
var su struct{ Counter uint64 }
|
||||||
|
su.Counter = atomic.AddUint64(&su.Counter, 1) // ERROR "direct assignment to atomic value"
|
||||||
|
z1 := atomic.AddUint64(&su.Counter, 1)
|
||||||
|
_ = z1 // Avoid err "z declared and not used"
|
||||||
|
|
||||||
|
var sp struct{ Counter *uint64 }
|
||||||
|
*sp.Counter = atomic.AddUint64(sp.Counter, 1) // ERROR "direct assignment to atomic value"
|
||||||
|
z2 := atomic.AddUint64(sp.Counter, 1)
|
||||||
|
_ = z2 // Avoid err "z declared and not used"
|
||||||
|
|
||||||
|
au := []uint64{10, 20}
|
||||||
|
au[0] = atomic.AddUint64(&au[0], 1) // ERROR "direct assignment to atomic value"
|
||||||
|
au[1] = atomic.AddUint64(&au[0], 1)
|
||||||
|
|
||||||
|
ap := []*uint64{&au[0], &au[1]}
|
||||||
|
*ap[0] = atomic.AddUint64(ap[0], 1) // ERROR "direct assignment to atomic value"
|
||||||
|
*ap[1] = atomic.AddUint64(ap[0], 1)
|
||||||
|
}
|
15
cmd/vet/test_buildtag.go
Normal file
15
cmd/vet/test_buildtag.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2013 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 contains tests for the buildtag checker.
|
||||||
|
|
||||||
|
// +build vet_test
|
||||||
|
// +builder // ERROR "possible malformed \+build comment"
|
||||||
|
// +build !ignore
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// +build toolate // ERROR "build comment appears too late in file"
|
||||||
|
|
||||||
|
var _ = 3
|
15
cmd/vet/test_buildtag_bad.go
Normal file
15
cmd/vet/test_buildtag_bad.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// This file contains misplaced or malformed build constraints.
|
||||||
|
// The Go tool will skip it, because the constraints are invalid.
|
||||||
|
// It serves only to test the tag checker during make test.
|
||||||
|
|
||||||
|
// Mention +build // ERROR "possible malformed \+build comment"
|
||||||
|
|
||||||
|
// +build !!bang // ERROR "invalid double negative in build constraint"
|
||||||
|
// +build @#$ // ERROR "invalid non-alphanumeric build constraint"
|
||||||
|
|
||||||
|
// +build toolate // ERROR "build comment appears too late in file"
|
||||||
|
package bad
|
||||||
|
|
||||||
|
// This is package 'bad' rather than 'main' so the erroneous build
|
||||||
|
// tag doesn't end up looking like a package doc for the vet command
|
||||||
|
// when examined by godoc.
|
2121
cmd/vet/test_deadcode.go
Normal file
2121
cmd/vet/test_deadcode.go
Normal file
File diff suppressed because it is too large
Load Diff
24
cmd/vet/test_method.go
Normal file
24
cmd/vet/test_method.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright 2010 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 contains tests for the canonical method checker.
|
||||||
|
|
||||||
|
// +build vet_test
|
||||||
|
|
||||||
|
// This file contains the code to check canonical methods.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MethodTest int
|
||||||
|
|
||||||
|
func (t *MethodTest) Scan(x fmt.ScanState, c byte) { // ERROR "should have signature Scan"
|
||||||
|
}
|
||||||
|
|
||||||
|
type MethodTestInterface interface {
|
||||||
|
ReadByte() byte // ERROR "should have signature ReadByte"
|
||||||
|
}
|
153
cmd/vet/test_print.go
Normal file
153
cmd/vet/test_print.go
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// Copyright 2010 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.
|
||||||
|
|
||||||
|
// +build vet_test
|
||||||
|
|
||||||
|
// This file contains tests for the printf checker.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unsafe" // just for test case printing unsafe.Pointer
|
||||||
|
)
|
||||||
|
|
||||||
|
func UnsafePointerPrintfTest() {
|
||||||
|
var up unsafe.Pointer
|
||||||
|
fmt.Printf("%p, %x %X", up, up, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error methods that do not satisfy the Error interface and should be checked.
|
||||||
|
type errorTest1 int
|
||||||
|
|
||||||
|
func (errorTest1) Error(...interface{}) string {
|
||||||
|
return "hi"
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorTest2 int // Analogous to testing's *T type.
|
||||||
|
func (errorTest2) Error(...interface{}) {
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorTest3 int
|
||||||
|
|
||||||
|
func (errorTest3) Error() { // No return value.
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorTest4 int
|
||||||
|
|
||||||
|
func (errorTest4) Error() int { // Different return type.
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorTest5 int
|
||||||
|
|
||||||
|
func (errorTest5) error() { // niladic; don't complain if no args (was bug)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function never executes, but it serves as a simple test for the program.
|
||||||
|
// Test with make test.
|
||||||
|
func PrintfTests() {
|
||||||
|
var b bool
|
||||||
|
var i int
|
||||||
|
var r rune
|
||||||
|
var s string
|
||||||
|
var x float64
|
||||||
|
var p *int
|
||||||
|
// Some good format/argtypes
|
||||||
|
fmt.Printf("")
|
||||||
|
fmt.Printf("%b %b", 3, i)
|
||||||
|
fmt.Printf("%c %c %c %c", 3, i, 'x', r)
|
||||||
|
fmt.Printf("%d %d", 3, i)
|
||||||
|
fmt.Printf("%e %e", 3e9, x)
|
||||||
|
fmt.Printf("%E %E", 3e9, x)
|
||||||
|
fmt.Printf("%f %f", 3e9, x)
|
||||||
|
fmt.Printf("%F %F", 3e9, x)
|
||||||
|
fmt.Printf("%g %g", 3e9, x)
|
||||||
|
fmt.Printf("%G %G", 3e9, x)
|
||||||
|
fmt.Printf("%o %o", 3, i)
|
||||||
|
fmt.Printf("%p %p", p, nil)
|
||||||
|
fmt.Printf("%q %q %q %q", 3, i, 'x', r)
|
||||||
|
fmt.Printf("%s %s", "hi", s)
|
||||||
|
fmt.Printf("%t %t", true, b)
|
||||||
|
fmt.Printf("%T %T", 3, i)
|
||||||
|
fmt.Printf("%U %U", 3, i)
|
||||||
|
fmt.Printf("%v %v", 3, i)
|
||||||
|
fmt.Printf("%x %x %x %x", 3, i, "hi", s)
|
||||||
|
fmt.Printf("%X %X %X %X", 3, i, "hi", s)
|
||||||
|
fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3)
|
||||||
|
// Some bad format/argTypes
|
||||||
|
fmt.Printf("%b", "hi") // ERROR "arg .hi. for printf verb %b of wrong type"
|
||||||
|
fmt.Printf("%c", 2.3) // ERROR "arg 2.3 for printf verb %c of wrong type"
|
||||||
|
fmt.Printf("%d", 2.3) // ERROR "arg 2.3 for printf verb %d of wrong type"
|
||||||
|
fmt.Printf("%e", "hi") // ERROR "arg .hi. for printf verb %e of wrong type"
|
||||||
|
fmt.Printf("%E", true) // ERROR "arg true for printf verb %E of wrong type"
|
||||||
|
fmt.Printf("%f", "hi") // ERROR "arg .hi. for printf verb %f of wrong type"
|
||||||
|
fmt.Printf("%F", 'x') // ERROR "arg 'x' for printf verb %F of wrong type"
|
||||||
|
fmt.Printf("%g", "hi") // ERROR "arg .hi. for printf verb %g of wrong type"
|
||||||
|
fmt.Printf("%G", i) // ERROR "arg i for printf verb %G of wrong type"
|
||||||
|
fmt.Printf("%o", x) // ERROR "arg x for printf verb %o of wrong type"
|
||||||
|
fmt.Printf("%p", 23) // ERROR "arg 23 for printf verb %p of wrong type"
|
||||||
|
fmt.Printf("%q", x) // ERROR "arg x for printf verb %q of wrong type"
|
||||||
|
fmt.Printf("%s", b) // ERROR "arg b for printf verb %s of wrong type"
|
||||||
|
fmt.Printf("%t", 23) // ERROR "arg 23 for printf verb %t of wrong type"
|
||||||
|
fmt.Printf("%U", x) // ERROR "arg x for printf verb %U of wrong type"
|
||||||
|
fmt.Printf("%x", nil) // ERROR "arg nil for printf verb %x of wrong type"
|
||||||
|
fmt.Printf("%X", 2.3) // ERROR "arg 2.3 for printf verb %X of wrong type"
|
||||||
|
fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type"
|
||||||
|
// TODO
|
||||||
|
fmt.Println() // not an error
|
||||||
|
fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call"
|
||||||
|
fmt.Printf("%s", "hi", 3) // ERROR "wrong number of args for format in Printf call"
|
||||||
|
fmt.Printf("%"+("s"), "hi", 3) // ERROR "wrong number of args for format in Printf call"
|
||||||
|
fmt.Printf("%s%%%d", "hi", 3) // correct
|
||||||
|
fmt.Printf("%08s", "woo") // correct
|
||||||
|
fmt.Printf("% 8s", "woo") // correct
|
||||||
|
fmt.Printf("%.*d", 3, 3) // correct
|
||||||
|
fmt.Printf("%.*d", 3, 3, 3) // ERROR "wrong number of args for format in Printf call"
|
||||||
|
fmt.Printf("%.*d", "hi", 3) // ERROR "arg .hi. for \* in printf format not of type int"
|
||||||
|
fmt.Printf("%.*d", i, 3) // correct
|
||||||
|
fmt.Printf("%.*d", s, 3) // ERROR "arg s for \* in printf format not of type int"
|
||||||
|
fmt.Printf("%q %q", multi()...) // ok
|
||||||
|
fmt.Printf("%#q", `blah`) // ok
|
||||||
|
printf("now is the time", "buddy") // ERROR "no formatting directive"
|
||||||
|
Printf("now is the time", "buddy") // ERROR "no formatting directive"
|
||||||
|
Printf("hi") // ok
|
||||||
|
const format = "%s %s\n"
|
||||||
|
Printf(format, "hi", "there")
|
||||||
|
Printf(format, "hi") // ERROR "wrong number of args for format in Printf call"
|
||||||
|
f := new(File)
|
||||||
|
f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call"
|
||||||
|
f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args for format in Warnf call"
|
||||||
|
f.Warnf(0, "%r", "hello") // ERROR "unrecognized printf verb"
|
||||||
|
f.Warnf(0, "%#s", "hello") // ERROR "unrecognized printf flag"
|
||||||
|
// Something that satisfies the error interface.
|
||||||
|
var e error
|
||||||
|
fmt.Println(e.Error()) // ok
|
||||||
|
// Something that looks like an error interface but isn't, such as the (*T).Error method
|
||||||
|
// in the testing package.
|
||||||
|
var et1 errorTest1
|
||||||
|
fmt.Println(et1.Error()) // ERROR "no args in Error call"
|
||||||
|
fmt.Println(et1.Error("hi")) // ok
|
||||||
|
fmt.Println(et1.Error("%d", 3)) // ERROR "possible formatting directive in Error call"
|
||||||
|
var et2 errorTest2
|
||||||
|
et2.Error() // ERROR "no args in Error call"
|
||||||
|
et2.Error("hi") // ok, not an error method.
|
||||||
|
et2.Error("%d", 3) // ERROR "possible formatting directive in Error call"
|
||||||
|
var et3 errorTest3
|
||||||
|
et3.Error() // ok, not an error method.
|
||||||
|
var et4 errorTest4
|
||||||
|
et4.Error() // ok, not an error method.
|
||||||
|
var et5 errorTest5
|
||||||
|
et5.error() // ok, not an error method.
|
||||||
|
}
|
||||||
|
|
||||||
|
// printf is used by the test.
|
||||||
|
func printf(format string, args ...interface{}) {
|
||||||
|
panic("don't call - testing only")
|
||||||
|
}
|
||||||
|
|
||||||
|
// multi is used by the test.
|
||||||
|
func multi() []interface{} {
|
||||||
|
panic("don't call - testing only")
|
||||||
|
}
|
61
cmd/vet/test_rangeloop.go
Normal file
61
cmd/vet/test_rangeloop.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2012 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 contains tests for the rangeloop checker.
|
||||||
|
|
||||||
|
// +build vet_test
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func RangeLoopTests() {
|
||||||
|
var s []int
|
||||||
|
for i, v := range s {
|
||||||
|
go func() {
|
||||||
|
println(i) // ERROR "range variable i enclosed by function"
|
||||||
|
println(v) // ERROR "range variable v enclosed by function"
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
for i, v := range s {
|
||||||
|
defer func() {
|
||||||
|
println(i) // ERROR "range variable i enclosed by function"
|
||||||
|
println(v) // ERROR "range variable v enclosed by function"
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
for i := range s {
|
||||||
|
go func() {
|
||||||
|
println(i) // ERROR "range variable i enclosed by function"
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
for _, v := range s {
|
||||||
|
go func() {
|
||||||
|
println(v) // ERROR "range variable v enclosed by function"
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
for i, v := range s {
|
||||||
|
go func() {
|
||||||
|
println(i, v)
|
||||||
|
}()
|
||||||
|
println("unfortunately, we don't catch the error above because of this statement")
|
||||||
|
}
|
||||||
|
for i, v := range s {
|
||||||
|
go func(i, v int) {
|
||||||
|
println(i, v)
|
||||||
|
}(i, v)
|
||||||
|
}
|
||||||
|
for i, v := range s {
|
||||||
|
i, v := i, v
|
||||||
|
go func() {
|
||||||
|
println(i, v)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// If the key of the range statement is not an identifier
|
||||||
|
// the code should not panic (it used to).
|
||||||
|
var x [2]int
|
||||||
|
var f int
|
||||||
|
for x[0], f = range s {
|
||||||
|
go func() {
|
||||||
|
_ = f // ERROR "range variable f enclosed by function"
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
15
cmd/vet/test_structtag.go
Normal file
15
cmd/vet/test_structtag.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2010 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 contains tests for the structtag checker.
|
||||||
|
|
||||||
|
// +build vet_test
|
||||||
|
|
||||||
|
// This file contains the test for canonical struct tags.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
type StructTagTest struct {
|
||||||
|
X int "hello" // ERROR "not compatible with reflect.StructTag.Get"
|
||||||
|
}
|
65
cmd/vet/test_taglit.go
Normal file
65
cmd/vet/test_taglit.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2012 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 contains tests for the untagged struct literal checker.
|
||||||
|
|
||||||
|
// +build vet_test
|
||||||
|
|
||||||
|
// This file contains the test for untagged struct literals.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"go/scanner"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Okay1 = []string{
|
||||||
|
"Name",
|
||||||
|
"Usage",
|
||||||
|
"DefValue",
|
||||||
|
}
|
||||||
|
|
||||||
|
var Okay2 = map[string]bool{
|
||||||
|
"Name": true,
|
||||||
|
"Usage": true,
|
||||||
|
"DefValue": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var Okay3 = struct {
|
||||||
|
X string
|
||||||
|
Y string
|
||||||
|
Z string
|
||||||
|
}{
|
||||||
|
"Name",
|
||||||
|
"Usage",
|
||||||
|
"DefValue",
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyStruct struct {
|
||||||
|
X string
|
||||||
|
Y string
|
||||||
|
Z string
|
||||||
|
}
|
||||||
|
|
||||||
|
var Okay4 = MyStruct{
|
||||||
|
"Name",
|
||||||
|
"Usage",
|
||||||
|
"DefValue",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Testing is awkward because we need to reference things from a separate package
|
||||||
|
// to trigger the warnings.
|
||||||
|
|
||||||
|
var BadStructLiteralUsedInTests = flag.Flag{ // ERROR "untagged fields"
|
||||||
|
"Name",
|
||||||
|
"Usage",
|
||||||
|
nil, // Value
|
||||||
|
"DefValue",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to test the check for slices and arrays: If that test is disabled and
|
||||||
|
// vet is run with --compositewhitelist=false, this line triggers an error.
|
||||||
|
// Clumsy but sufficient.
|
||||||
|
var scannerErrorListTest = scanner.ErrorList{nil, nil}
|
185
cmd/vet/types.go
Normal file
185
cmd/vet/types.go
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
// Copyright 2010 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.
|
||||||
|
|
||||||
|
// +build gotypes
|
||||||
|
|
||||||
|
// This file contains the pieces of the tool that require the go/types package.
|
||||||
|
// To compile this file, you must first run
|
||||||
|
// $ go get code.google.com/p/go.tools/go/types
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/go/exact"
|
||||||
|
"code.google.com/p/go.tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Type is equivalent to types.Type. Repeating it here allows us to avoid
|
||||||
|
// having main depend on the go/types package.
|
||||||
|
type Type interface {
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExactValue is equivalent to exact.Value. Repeating it here allows us to
|
||||||
|
// avoid having main depend on the go/exact package.
|
||||||
|
type ExactValue interface {
|
||||||
|
Kind() exact.Kind
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
|
||||||
|
pkg.types = make(map[ast.Expr]Type)
|
||||||
|
pkg.values = make(map[ast.Expr]ExactValue)
|
||||||
|
exprFn := func(x ast.Expr, typ types.Type, val exact.Value) {
|
||||||
|
pkg.types[x] = typ
|
||||||
|
if val != nil {
|
||||||
|
pkg.values[x] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// By providing the Context with our own error function, it will continue
|
||||||
|
// past the first error. There is no need for that function to do anything.
|
||||||
|
context := types.Context{
|
||||||
|
Expr: exprFn,
|
||||||
|
Error: func(error) {},
|
||||||
|
}
|
||||||
|
_, err := context.Check(fs, astFiles)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// isStruct reports whether the composite literal c is a struct.
|
||||||
|
// If it is not (probably a struct), it returns a printable form of the type.
|
||||||
|
func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
|
||||||
|
// Check that the CompositeLit's type is a slice or array (which needs no tag), if possible.
|
||||||
|
typ := pkg.types[c]
|
||||||
|
// If it's a named type, pull out the underlying type.
|
||||||
|
actual := typ
|
||||||
|
if namedType, ok := typ.(*types.NamedType); ok {
|
||||||
|
actual = namedType.Underlying
|
||||||
|
}
|
||||||
|
if actual == nil {
|
||||||
|
// No type information available. Assume true, so we do the check.
|
||||||
|
return true, ""
|
||||||
|
}
|
||||||
|
switch actual.(type) {
|
||||||
|
case *types.Struct:
|
||||||
|
return true, typ.String()
|
||||||
|
default:
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool {
|
||||||
|
// TODO: for now, we can only test builtin types and untyped constants.
|
||||||
|
typ := f.pkg.types[arg]
|
||||||
|
if typ == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
basic, ok := typ.(*types.Basic)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
switch basic.Kind {
|
||||||
|
case types.Bool:
|
||||||
|
return t&argBool != 0
|
||||||
|
case types.Int, types.Int8, types.Int16, types.Int32, types.Int64:
|
||||||
|
fallthrough
|
||||||
|
case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr:
|
||||||
|
return t&argInt != 0
|
||||||
|
case types.Float32, types.Float64, types.Complex64, types.Complex128:
|
||||||
|
return t&argFloat != 0
|
||||||
|
case types.String:
|
||||||
|
return t&argString != 0
|
||||||
|
case types.UnsafePointer:
|
||||||
|
return t&(argPointer|argInt) != 0
|
||||||
|
case types.UntypedBool:
|
||||||
|
return t&argBool != 0
|
||||||
|
case types.UntypedComplex:
|
||||||
|
return t&argFloat != 0
|
||||||
|
case types.UntypedFloat:
|
||||||
|
// If it's integral, we can use an int format.
|
||||||
|
switch f.pkg.values[arg].Kind() {
|
||||||
|
case exact.Int:
|
||||||
|
return t&(argInt|argFloat) != 0
|
||||||
|
}
|
||||||
|
return t&argFloat != 0
|
||||||
|
case types.UntypedInt:
|
||||||
|
return t&argInt != 0
|
||||||
|
case types.UntypedRune:
|
||||||
|
return t&(argInt|argRune) != 0
|
||||||
|
case types.UntypedString:
|
||||||
|
return t&argString != 0
|
||||||
|
case types.UntypedNil:
|
||||||
|
return t&argPointer != 0 // TODO?
|
||||||
|
case types.Invalid:
|
||||||
|
if *verbose {
|
||||||
|
f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", arg)
|
||||||
|
}
|
||||||
|
return true // Probably a type check problem.
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// numArgsInSignature tells how many formal arguments the function type
|
||||||
|
// being called has.
|
||||||
|
func (f *File) numArgsInSignature(call *ast.CallExpr) int {
|
||||||
|
// Check the type of the function or method declaration
|
||||||
|
typ := f.pkg.types[call.Fun]
|
||||||
|
if typ == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
// The type must be a signature, but be sure for safety.
|
||||||
|
sig, ok := typ.(*types.Signature)
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return len(sig.Params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isErrorMethodCall reports whether the call is of a method with signature
|
||||||
|
// func Error() string
|
||||||
|
// where "string" is the universe's string type. We know the method is called "Error".
|
||||||
|
func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
|
||||||
|
// Is it a selector expression? Otherwise it's a function call, not a method call.
|
||||||
|
sel, ok := call.Fun.(*ast.SelectorExpr)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// The package is type-checked, so if there are no arguments, we're done.
|
||||||
|
if len(call.Args) > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Check the type of the method declaration
|
||||||
|
typ := f.pkg.types[sel]
|
||||||
|
if typ == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// The type must be a signature, but be sure for safety.
|
||||||
|
sig, ok := typ.(*types.Signature)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// There must be a receiver for it to be a method call. Otherwise it is
|
||||||
|
// a function, not something that satisfies the error interface.
|
||||||
|
if sig.Recv == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// There must be no arguments. Already verified by type checking, but be thorough.
|
||||||
|
if len(sig.Params) > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Finally the real questions.
|
||||||
|
// There must be one result.
|
||||||
|
if len(sig.Results) != 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// It must have return type "string" from the universe.
|
||||||
|
result := sig.Results[0].Type
|
||||||
|
if types.IsIdentical(result, types.Typ[types.String]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
50
cmd/vet/typestub.go
Normal file
50
cmd/vet/typestub.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2010 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.
|
||||||
|
|
||||||
|
// +build !gotypes
|
||||||
|
|
||||||
|
// This file contains stubs for the pieces of the tool that require the go/types package,
|
||||||
|
// to be used if go/types is not available.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Type is equivalent to go/types.Type. Repeating it here allows us to avoid
|
||||||
|
// having main depend on the go/types package.
|
||||||
|
type Type interface {
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExactValue is a stub for exact.Value. Stubbing it here allows us to
|
||||||
|
// avoid having main depend on the go/exact package.
|
||||||
|
type ExactValue interface {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
|
||||||
|
return true, "" // Assume true, so we do the check.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool {
|
||||||
|
return true // We can't tell without types.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) numArgsInSignature(call *ast.CallExpr) int {
|
||||||
|
return 0 // We don't know.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
|
||||||
|
// Is it a selector expression? Otherwise it's a function call, not a method call.
|
||||||
|
if _, ok := call.Fun.(*ast.SelectorExpr); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true // Best guess we can make without types.
|
||||||
|
}
|
748
go/exact/exact.go
Normal file
748
go/exact/exact.go
Normal file
@ -0,0 +1,748 @@
|
|||||||
|
// Copyright 2013 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 exact implements mathematically exact values
|
||||||
|
// and operations for all Go basic types.
|
||||||
|
//
|
||||||
|
package exact
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/token"
|
||||||
|
"math/big"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Kind specifies the kind of value represented by a Value.
|
||||||
|
type Kind int
|
||||||
|
|
||||||
|
// Implementation note: Kinds must be enumerated in
|
||||||
|
// order of increasing "complexity" (used by match).
|
||||||
|
|
||||||
|
const (
|
||||||
|
// unknown values
|
||||||
|
Unknown Kind = iota
|
||||||
|
|
||||||
|
// non-numeric values
|
||||||
|
Nil
|
||||||
|
Bool
|
||||||
|
String
|
||||||
|
|
||||||
|
// numeric values
|
||||||
|
Int
|
||||||
|
Float
|
||||||
|
Complex
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Value represents a mathematically precise value of a given Kind.
|
||||||
|
type Value interface {
|
||||||
|
// Kind returns the value kind; it is always the smallest
|
||||||
|
// kind in which the value can be represented exactly.
|
||||||
|
Kind() Kind
|
||||||
|
|
||||||
|
// String returns a human-readable form of the value.
|
||||||
|
String() string
|
||||||
|
|
||||||
|
// Prevent external implementations.
|
||||||
|
implementsValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) Handle unknown values more consistently. Some operations
|
||||||
|
// accept unknowns, some don't.
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Implementations
|
||||||
|
|
||||||
|
type (
|
||||||
|
unknownVal struct{}
|
||||||
|
nilVal struct{}
|
||||||
|
boolVal bool
|
||||||
|
stringVal string
|
||||||
|
int64Val int64
|
||||||
|
intVal struct{ val *big.Int }
|
||||||
|
floatVal struct{ val *big.Rat }
|
||||||
|
complexVal struct{ re, im *big.Rat }
|
||||||
|
)
|
||||||
|
|
||||||
|
func (unknownVal) Kind() Kind { return Unknown }
|
||||||
|
func (nilVal) Kind() Kind { return Nil }
|
||||||
|
func (boolVal) Kind() Kind { return Bool }
|
||||||
|
func (stringVal) Kind() Kind { return String }
|
||||||
|
func (int64Val) Kind() Kind { return Int }
|
||||||
|
func (intVal) Kind() Kind { return Int }
|
||||||
|
func (floatVal) Kind() Kind { return Float }
|
||||||
|
func (complexVal) Kind() Kind { return Complex }
|
||||||
|
|
||||||
|
func (unknownVal) String() string { return "unknown" }
|
||||||
|
func (nilVal) String() string { return "nil" }
|
||||||
|
func (x boolVal) String() string { return fmt.Sprintf("%v", bool(x)) }
|
||||||
|
func (x stringVal) String() string { return strconv.Quote(string(x)) }
|
||||||
|
func (x int64Val) String() string { return strconv.FormatInt(int64(x), 10) }
|
||||||
|
func (x intVal) String() string { return x.val.String() }
|
||||||
|
func (x floatVal) String() string { return x.val.String() }
|
||||||
|
func (x complexVal) String() string { return fmt.Sprintf("(%s + %si)", x.re, x.im) }
|
||||||
|
|
||||||
|
func (unknownVal) implementsValue() {}
|
||||||
|
func (nilVal) implementsValue() {}
|
||||||
|
func (boolVal) implementsValue() {}
|
||||||
|
func (stringVal) implementsValue() {}
|
||||||
|
func (int64Val) implementsValue() {}
|
||||||
|
func (intVal) implementsValue() {}
|
||||||
|
func (floatVal) implementsValue() {}
|
||||||
|
func (complexVal) implementsValue() {}
|
||||||
|
|
||||||
|
// int64 bounds
|
||||||
|
var (
|
||||||
|
minInt64 = big.NewInt(-1 << 63)
|
||||||
|
maxInt64 = big.NewInt(1<<63 - 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
func normInt(x *big.Int) Value {
|
||||||
|
if minInt64.Cmp(x) <= 0 && x.Cmp(maxInt64) <= 0 {
|
||||||
|
return int64Val(x.Int64())
|
||||||
|
}
|
||||||
|
return intVal{x}
|
||||||
|
}
|
||||||
|
|
||||||
|
func normFloat(x *big.Rat) Value {
|
||||||
|
if x.IsInt() {
|
||||||
|
return normInt(x.Num())
|
||||||
|
}
|
||||||
|
return floatVal{x}
|
||||||
|
}
|
||||||
|
|
||||||
|
func normComplex(re, im *big.Rat) Value {
|
||||||
|
if im.Sign() == 0 {
|
||||||
|
return normFloat(re)
|
||||||
|
}
|
||||||
|
return complexVal{re, im}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Factories
|
||||||
|
|
||||||
|
// MakeUnknown returns the Unknown value.
|
||||||
|
func MakeUnknown() Value { return unknownVal{} }
|
||||||
|
|
||||||
|
// MakeNil returns the Nil value.
|
||||||
|
func MakeNil() Value { return nilVal{} }
|
||||||
|
|
||||||
|
// MakeBool returns the Bool value for x.
|
||||||
|
func MakeBool(b bool) Value { return boolVal(b) }
|
||||||
|
|
||||||
|
// MakeString returns the String value for x.
|
||||||
|
func MakeString(s string) Value { return stringVal(s) }
|
||||||
|
|
||||||
|
// MakeInt64 returns the Int value for x.
|
||||||
|
func MakeInt64(x int64) Value { return int64Val(x) }
|
||||||
|
|
||||||
|
// MakeUint64 returns the Int value for x.
|
||||||
|
func MakeUint64(x uint64) Value { return normInt(new(big.Int).SetUint64(x)) }
|
||||||
|
|
||||||
|
// MakeFloat64 returns the numeric value for x.
|
||||||
|
// If x is not finite, the result is unknown.
|
||||||
|
func MakeFloat64(x float64) Value {
|
||||||
|
f := new(big.Rat).SetFloat64(x)
|
||||||
|
if f != nil {
|
||||||
|
return normFloat(f)
|
||||||
|
}
|
||||||
|
return unknownVal{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeFromLiteral returns the corresponding literal value.
|
||||||
|
// If the literal has illegal format, the result is nil.
|
||||||
|
func MakeFromLiteral(lit string, tok token.Token) Value {
|
||||||
|
switch tok {
|
||||||
|
case token.INT:
|
||||||
|
if x, err := strconv.ParseInt(lit, 0, 64); err == nil {
|
||||||
|
return int64Val(x)
|
||||||
|
}
|
||||||
|
if x, ok := new(big.Int).SetString(lit, 0); ok {
|
||||||
|
return intVal{x}
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.FLOAT:
|
||||||
|
if x, ok := new(big.Rat).SetString(lit); ok {
|
||||||
|
return normFloat(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.IMAG:
|
||||||
|
if n := len(lit); n > 0 && lit[n-1] == 'i' {
|
||||||
|
if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok {
|
||||||
|
return normComplex(big.NewRat(0, 1), im)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.CHAR:
|
||||||
|
if n := len(lit); n >= 2 {
|
||||||
|
if code, _, _, err := strconv.UnquoteChar(lit[1:n-1], '\''); err == nil {
|
||||||
|
return int64Val(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.STRING:
|
||||||
|
if s, err := strconv.Unquote(lit); err == nil {
|
||||||
|
return stringVal(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) should we instead a) return unknown, or b) an error?
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Accessors
|
||||||
|
|
||||||
|
// BoolVal returns the Go boolean value of x, which must be a Bool.
|
||||||
|
func BoolVal(x Value) bool { return bool(x.(boolVal)) }
|
||||||
|
|
||||||
|
// StringVal returns the Go string value of x, which must be a String.
|
||||||
|
func StringVal(x Value) string { return string(x.(stringVal)) }
|
||||||
|
|
||||||
|
// Int64Val returns the Go int64 value of x and whether the result is exact;
|
||||||
|
// x must be an Int. If the result is not exact, its value is undefined.
|
||||||
|
func Int64Val(x Value) (int64, bool) {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int64Val:
|
||||||
|
return int64(x), true
|
||||||
|
case intVal:
|
||||||
|
return x.val.Int64(), x.val.BitLen() <= 63
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("invalid Int64Val(%v)", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64Val returns the Go uint64 value of x and whether the result is exact;
|
||||||
|
// x must be an Int. If the result is not exact, its value is undefined.
|
||||||
|
func Uint64Val(x Value) (uint64, bool) {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int64Val:
|
||||||
|
return uint64(x), x >= 0
|
||||||
|
case intVal:
|
||||||
|
return x.val.Uint64(), x.val.Sign() >= 0 && x.val.BitLen() <= 64
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("invalid Uint64Val(%v)", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Val returns the nearest Go float64 value of x and whether the result is exact;
|
||||||
|
// x must be numeric but not Complex.
|
||||||
|
func Float64Val(x Value) (float64, bool) {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int64Val:
|
||||||
|
f := float64(int64(x))
|
||||||
|
return f, int64Val(f) == x
|
||||||
|
case intVal:
|
||||||
|
return new(big.Rat).SetFrac(x.val, int1).Float64()
|
||||||
|
case floatVal:
|
||||||
|
return x.val.Float64()
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("invalid Float64Val(%v)", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BitLen() returns the number of bits required to represent
|
||||||
|
// the absolute value x in binary representation; x must be an Int.
|
||||||
|
func BitLen(x Value) int {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int64Val:
|
||||||
|
return new(big.Int).SetInt64(int64(x)).BitLen()
|
||||||
|
case intVal:
|
||||||
|
return x.val.BitLen()
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("invalid BitLen(%v)", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign returns -1, 0, or 1 depending on whether
|
||||||
|
// x < 0, x == 0, or x > 0. For complex values z,
|
||||||
|
// the sign is 0 if z == 0, otherwise it is != 0.
|
||||||
|
// For unknown values, the sign is always 1 (this
|
||||||
|
// helps avoiding "division by zero errors"). For
|
||||||
|
// all other values, Sign panics.
|
||||||
|
//
|
||||||
|
func Sign(x Value) int {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case unknownVal:
|
||||||
|
return 1
|
||||||
|
case int64Val:
|
||||||
|
switch {
|
||||||
|
case x < 0:
|
||||||
|
return -1
|
||||||
|
case x > 0:
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
case intVal:
|
||||||
|
return x.val.Sign()
|
||||||
|
case floatVal:
|
||||||
|
return x.val.Sign()
|
||||||
|
case complexVal:
|
||||||
|
return x.re.Sign() | x.im.Sign()
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("invalid Sign(%v)", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Support for assembling/disassembling complex numbers
|
||||||
|
|
||||||
|
// MakeImag returns the numeric value x*i (possibly 0);
|
||||||
|
// x must be numeric but not Complex.
|
||||||
|
// If x is unknown, the result is unknown.
|
||||||
|
func MakeImag(x Value) Value {
|
||||||
|
var im *big.Rat
|
||||||
|
switch x := x.(type) {
|
||||||
|
case unknownVal:
|
||||||
|
return x
|
||||||
|
case int64Val:
|
||||||
|
im = big.NewRat(int64(x), 1)
|
||||||
|
case intVal:
|
||||||
|
im = new(big.Rat).SetFrac(x.val, int1)
|
||||||
|
case floatVal:
|
||||||
|
im = x.val
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("invalid MakeImag(%v)", x))
|
||||||
|
}
|
||||||
|
return normComplex(rat0, im)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real returns the real part of x, which must be a numeric value.
|
||||||
|
// If x is unknown, the result is unknown.
|
||||||
|
func Real(x Value) Value {
|
||||||
|
if z, ok := x.(complexVal); ok {
|
||||||
|
return normFloat(z.re)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Imag returns the imaginary part of x, which must be a numeric value.
|
||||||
|
// If x is unknown, the result is 0.
|
||||||
|
func Imag(x Value) Value {
|
||||||
|
if z, ok := x.(complexVal); ok {
|
||||||
|
return normFloat(z.im)
|
||||||
|
}
|
||||||
|
return int64Val(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Operations
|
||||||
|
|
||||||
|
// is32bit reports whether x can be represented using 32 bits.
|
||||||
|
func is32bit(x int64) bool {
|
||||||
|
const s = 32
|
||||||
|
return -1<<(s-1) <= x && x <= 1<<(s-1)-1
|
||||||
|
}
|
||||||
|
|
||||||
|
// is63bit reports whether x can be represented using 63 bits.
|
||||||
|
func is63bit(x int64) bool {
|
||||||
|
const s = 63
|
||||||
|
return -1<<(s-1) <= x && x <= 1<<(s-1)-1
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnaryOp returns the result of the unary expression op y.
|
||||||
|
// The operation must be defined for the operand.
|
||||||
|
// If size >= 0 it specifies the ^ (xor) result size in bytes.
|
||||||
|
//
|
||||||
|
func UnaryOp(op token.Token, y Value, size int) Value {
|
||||||
|
switch op {
|
||||||
|
case token.ADD:
|
||||||
|
switch y.(type) {
|
||||||
|
case unknownVal, int64Val, intVal, floatVal, complexVal:
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.SUB:
|
||||||
|
switch y := y.(type) {
|
||||||
|
case int64Val:
|
||||||
|
if z := -y; z != y {
|
||||||
|
return z // no overflow
|
||||||
|
}
|
||||||
|
return normInt(new(big.Int).Neg(big.NewInt(int64(y))))
|
||||||
|
case intVal:
|
||||||
|
return normInt(new(big.Int).Neg(y.val))
|
||||||
|
case floatVal:
|
||||||
|
return normFloat(new(big.Rat).Neg(y.val))
|
||||||
|
case complexVal:
|
||||||
|
return normComplex(new(big.Rat).Neg(y.re), new(big.Rat).Neg(y.im))
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.XOR:
|
||||||
|
var z big.Int
|
||||||
|
switch y := y.(type) {
|
||||||
|
case int64Val:
|
||||||
|
z.Not(big.NewInt(int64(y)))
|
||||||
|
case intVal:
|
||||||
|
z.Not(y.val)
|
||||||
|
default:
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
// For unsigned types, the result will be negative and
|
||||||
|
// thus "too large": We must limit the result size to
|
||||||
|
// the type's size.
|
||||||
|
if size >= 0 {
|
||||||
|
s := uint(size) * 8
|
||||||
|
z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s)) // z &^= (-1)<<s
|
||||||
|
}
|
||||||
|
return normInt(&z)
|
||||||
|
|
||||||
|
case token.NOT:
|
||||||
|
return !y.(boolVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
Error:
|
||||||
|
panic(fmt.Sprintf("invalid unary operation %s%v", op, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
int1 = big.NewInt(1)
|
||||||
|
rat0 = big.NewRat(0, 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
func ord(x Value) int {
|
||||||
|
switch x.(type) {
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
case int64Val:
|
||||||
|
return 1
|
||||||
|
case intVal:
|
||||||
|
return 2
|
||||||
|
case floatVal:
|
||||||
|
return 3
|
||||||
|
case complexVal:
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// match returns the matching representation (same type) with the
|
||||||
|
// smallest complexity for two values x and y. If one of them is
|
||||||
|
// numeric, both of them must be numeric.
|
||||||
|
//
|
||||||
|
func match(x, y Value) (_, _ Value) {
|
||||||
|
if ord(x) > ord(y) {
|
||||||
|
y, x = match(y, x)
|
||||||
|
return x, y
|
||||||
|
}
|
||||||
|
// ord(x) <= ord(y)
|
||||||
|
|
||||||
|
switch x := x.(type) {
|
||||||
|
case unknownVal, nilVal, boolVal, stringVal, complexVal:
|
||||||
|
return x, y
|
||||||
|
|
||||||
|
case int64Val:
|
||||||
|
switch y := y.(type) {
|
||||||
|
case int64Val:
|
||||||
|
return x, y
|
||||||
|
case intVal:
|
||||||
|
return intVal{big.NewInt(int64(x))}, y
|
||||||
|
case floatVal:
|
||||||
|
return floatVal{big.NewRat(int64(x), 1)}, y
|
||||||
|
case complexVal:
|
||||||
|
return complexVal{big.NewRat(int64(x), 1), rat0}, y
|
||||||
|
}
|
||||||
|
|
||||||
|
case intVal:
|
||||||
|
switch y := y.(type) {
|
||||||
|
case intVal:
|
||||||
|
return x, y
|
||||||
|
case floatVal:
|
||||||
|
return floatVal{new(big.Rat).SetFrac(x.val, int1)}, y
|
||||||
|
case complexVal:
|
||||||
|
return complexVal{new(big.Rat).SetFrac(x.val, int1), rat0}, y
|
||||||
|
}
|
||||||
|
|
||||||
|
case floatVal:
|
||||||
|
switch y := y.(type) {
|
||||||
|
case floatVal:
|
||||||
|
return x, y
|
||||||
|
case complexVal:
|
||||||
|
return complexVal{x.val, rat0}, y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinaryOp returns the result of the binary expression x op y.
|
||||||
|
// The operation must be defined for the operands.
|
||||||
|
// To force integer division of Int operands, use op == token.QUO_ASSIGN
|
||||||
|
// instead of token.QUO; the result is guaranteed to be Int in this case.
|
||||||
|
// Division by zero leads to a run-time panic.
|
||||||
|
//
|
||||||
|
func BinaryOp(x Value, op token.Token, y Value) Value {
|
||||||
|
x, y = match(x, y)
|
||||||
|
|
||||||
|
switch x := x.(type) {
|
||||||
|
case unknownVal:
|
||||||
|
return x
|
||||||
|
|
||||||
|
case boolVal:
|
||||||
|
y := y.(boolVal)
|
||||||
|
switch op {
|
||||||
|
case token.LAND:
|
||||||
|
return x && y
|
||||||
|
case token.LOR:
|
||||||
|
return x || y
|
||||||
|
}
|
||||||
|
|
||||||
|
case int64Val:
|
||||||
|
a := int64(x)
|
||||||
|
b := int64(y.(int64Val))
|
||||||
|
var c int64
|
||||||
|
switch op {
|
||||||
|
case token.ADD:
|
||||||
|
if !is63bit(a) || !is63bit(b) {
|
||||||
|
return normInt(new(big.Int).Add(big.NewInt(a), big.NewInt(b)))
|
||||||
|
}
|
||||||
|
c = a + b
|
||||||
|
case token.SUB:
|
||||||
|
if !is63bit(a) || !is63bit(b) {
|
||||||
|
return normInt(new(big.Int).Sub(big.NewInt(a), big.NewInt(b)))
|
||||||
|
}
|
||||||
|
c = a - b
|
||||||
|
case token.MUL:
|
||||||
|
if !is32bit(a) || !is32bit(b) {
|
||||||
|
return normInt(new(big.Int).Mul(big.NewInt(a), big.NewInt(b)))
|
||||||
|
}
|
||||||
|
c = a * b
|
||||||
|
case token.QUO:
|
||||||
|
return normFloat(new(big.Rat).SetFrac(big.NewInt(a), big.NewInt(b)))
|
||||||
|
case token.QUO_ASSIGN: // force integer division
|
||||||
|
c = a / b
|
||||||
|
case token.REM:
|
||||||
|
c = a % b
|
||||||
|
case token.AND:
|
||||||
|
c = a & b
|
||||||
|
case token.OR:
|
||||||
|
c = a | b
|
||||||
|
case token.XOR:
|
||||||
|
c = a ^ b
|
||||||
|
case token.AND_NOT:
|
||||||
|
c = a &^ b
|
||||||
|
default:
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
return int64Val(c)
|
||||||
|
|
||||||
|
case intVal:
|
||||||
|
a := x.val
|
||||||
|
b := y.(intVal).val
|
||||||
|
var c big.Int
|
||||||
|
switch op {
|
||||||
|
case token.ADD:
|
||||||
|
c.Add(a, b)
|
||||||
|
case token.SUB:
|
||||||
|
c.Sub(a, b)
|
||||||
|
case token.MUL:
|
||||||
|
c.Mul(a, b)
|
||||||
|
case token.QUO:
|
||||||
|
return normFloat(new(big.Rat).SetFrac(a, b))
|
||||||
|
case token.QUO_ASSIGN: // force integer division
|
||||||
|
c.Quo(a, b)
|
||||||
|
case token.REM:
|
||||||
|
c.Rem(a, b)
|
||||||
|
case token.AND:
|
||||||
|
c.And(a, b)
|
||||||
|
case token.OR:
|
||||||
|
c.Or(a, b)
|
||||||
|
case token.XOR:
|
||||||
|
c.Xor(a, b)
|
||||||
|
case token.AND_NOT:
|
||||||
|
c.AndNot(a, b)
|
||||||
|
default:
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
return normInt(&c)
|
||||||
|
|
||||||
|
case floatVal:
|
||||||
|
a := x.val
|
||||||
|
b := y.(floatVal).val
|
||||||
|
var c big.Rat
|
||||||
|
switch op {
|
||||||
|
case token.ADD:
|
||||||
|
c.Add(a, b)
|
||||||
|
case token.SUB:
|
||||||
|
c.Sub(a, b)
|
||||||
|
case token.MUL:
|
||||||
|
c.Mul(a, b)
|
||||||
|
case token.QUO:
|
||||||
|
c.Quo(a, b)
|
||||||
|
default:
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
return normFloat(&c)
|
||||||
|
|
||||||
|
case complexVal:
|
||||||
|
y := y.(complexVal)
|
||||||
|
a, b := x.re, x.im
|
||||||
|
c, d := y.re, y.im
|
||||||
|
var re, im big.Rat
|
||||||
|
switch op {
|
||||||
|
case token.ADD:
|
||||||
|
// (a+c) + i(b+d)
|
||||||
|
re.Add(a, c)
|
||||||
|
im.Add(b, d)
|
||||||
|
case token.SUB:
|
||||||
|
// (a-c) + i(b-d)
|
||||||
|
re.Sub(a, c)
|
||||||
|
im.Sub(b, d)
|
||||||
|
case token.MUL:
|
||||||
|
// (ac-bd) + i(bc+ad)
|
||||||
|
var ac, bd, bc, ad big.Rat
|
||||||
|
ac.Mul(a, c)
|
||||||
|
bd.Mul(b, d)
|
||||||
|
bc.Mul(b, c)
|
||||||
|
ad.Mul(a, d)
|
||||||
|
re.Sub(&ac, &bd)
|
||||||
|
im.Add(&bc, &ad)
|
||||||
|
case token.QUO:
|
||||||
|
// (ac+bd)/s + i(bc-ad)/s, with s = cc + dd
|
||||||
|
var ac, bd, bc, ad, s big.Rat
|
||||||
|
ac.Mul(a, c)
|
||||||
|
bd.Mul(b, d)
|
||||||
|
bc.Mul(b, c)
|
||||||
|
ad.Mul(a, d)
|
||||||
|
s.Add(c.Mul(c, c), d.Mul(d, d))
|
||||||
|
re.Add(&ac, &bd)
|
||||||
|
re.Quo(&re, &s)
|
||||||
|
im.Sub(&bc, &ad)
|
||||||
|
im.Quo(&im, &s)
|
||||||
|
default:
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
return normComplex(&re, &im)
|
||||||
|
|
||||||
|
case stringVal:
|
||||||
|
if op == token.ADD {
|
||||||
|
return x + y.(stringVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Error:
|
||||||
|
panic(fmt.Sprintf("invalid binary operation %v %s %v", x, op, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift returns the result of the shift expression x op s
|
||||||
|
// with op == token.SHL or token.SHR (<< or >>). x must be
|
||||||
|
// an Int.
|
||||||
|
//
|
||||||
|
func Shift(x Value, op token.Token, s uint) Value {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case unknownVal:
|
||||||
|
return x
|
||||||
|
|
||||||
|
case int64Val:
|
||||||
|
if s == 0 {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
switch op {
|
||||||
|
case token.SHL:
|
||||||
|
z := big.NewInt(int64(x))
|
||||||
|
return normInt(z.Lsh(z, s))
|
||||||
|
case token.SHR:
|
||||||
|
return x >> s
|
||||||
|
}
|
||||||
|
|
||||||
|
case intVal:
|
||||||
|
if s == 0 {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
var z big.Int
|
||||||
|
switch op {
|
||||||
|
case token.SHL:
|
||||||
|
return normInt(z.Lsh(x.val, s))
|
||||||
|
case token.SHR:
|
||||||
|
return normInt(z.Rsh(x.val, s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Sprintf("invalid shift %v %s %d", x, op, s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmpZero(x int, op token.Token) bool {
|
||||||
|
switch op {
|
||||||
|
case token.EQL:
|
||||||
|
return x == 0
|
||||||
|
case token.NEQ:
|
||||||
|
return x != 0
|
||||||
|
case token.LSS:
|
||||||
|
return x < 0
|
||||||
|
case token.LEQ:
|
||||||
|
return x <= 0
|
||||||
|
case token.GTR:
|
||||||
|
return x > 0
|
||||||
|
case token.GEQ:
|
||||||
|
return x >= 0
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare returns the result of the comparison x op y.
|
||||||
|
// The comparison must be defined for the operands.
|
||||||
|
//
|
||||||
|
func Compare(x Value, op token.Token, y Value) bool {
|
||||||
|
x, y = match(x, y)
|
||||||
|
|
||||||
|
switch x := x.(type) {
|
||||||
|
case unknownVal:
|
||||||
|
return true
|
||||||
|
|
||||||
|
case boolVal:
|
||||||
|
y := y.(boolVal)
|
||||||
|
switch op {
|
||||||
|
case token.EQL:
|
||||||
|
return x == y
|
||||||
|
case token.NEQ:
|
||||||
|
return x != y
|
||||||
|
}
|
||||||
|
|
||||||
|
case int64Val:
|
||||||
|
y := y.(int64Val)
|
||||||
|
switch op {
|
||||||
|
case token.EQL:
|
||||||
|
return x == y
|
||||||
|
case token.NEQ:
|
||||||
|
return x != y
|
||||||
|
case token.LSS:
|
||||||
|
return x < y
|
||||||
|
case token.LEQ:
|
||||||
|
return x <= y
|
||||||
|
case token.GTR:
|
||||||
|
return x > y
|
||||||
|
case token.GEQ:
|
||||||
|
return x >= y
|
||||||
|
}
|
||||||
|
|
||||||
|
case intVal:
|
||||||
|
return cmpZero(x.val.Cmp(y.(intVal).val), op)
|
||||||
|
|
||||||
|
case floatVal:
|
||||||
|
return cmpZero(x.val.Cmp(y.(floatVal).val), op)
|
||||||
|
|
||||||
|
case complexVal:
|
||||||
|
y := y.(complexVal)
|
||||||
|
re := x.re.Cmp(y.re)
|
||||||
|
im := x.im.Cmp(y.im)
|
||||||
|
switch op {
|
||||||
|
case token.EQL:
|
||||||
|
return re == 0 && im == 0
|
||||||
|
case token.NEQ:
|
||||||
|
return re != 0 || im != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
case stringVal:
|
||||||
|
y := y.(stringVal)
|
||||||
|
switch op {
|
||||||
|
case token.EQL:
|
||||||
|
return x == y
|
||||||
|
case token.NEQ:
|
||||||
|
return x != y
|
||||||
|
case token.LSS:
|
||||||
|
return x < y
|
||||||
|
case token.LEQ:
|
||||||
|
return x <= y
|
||||||
|
case token.GTR:
|
||||||
|
return x > y
|
||||||
|
case token.GEQ:
|
||||||
|
return x >= y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Sprintf("invalid comparison %v %s %v", x, op, y))
|
||||||
|
}
|
186
go/exact/exact_test.go
Normal file
186
go/exact/exact_test.go
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
// Copyright 2013 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 exact
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/token"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(gri) expand this test framework
|
||||||
|
|
||||||
|
var tests = []string{
|
||||||
|
// unary operations
|
||||||
|
`+ 0 = 0`,
|
||||||
|
`- 1 = -1`,
|
||||||
|
|
||||||
|
`! true = false`,
|
||||||
|
`! false = true`,
|
||||||
|
// etc.
|
||||||
|
|
||||||
|
// binary operations
|
||||||
|
`"" + "" = ""`,
|
||||||
|
`"foo" + "" = "foo"`,
|
||||||
|
`"" + "bar" = "bar"`,
|
||||||
|
`"foo" + "bar" = "foobar"`,
|
||||||
|
|
||||||
|
`0 + 0 = 0`,
|
||||||
|
`0 + 0.1 = 0.1`,
|
||||||
|
`0 + 0.1i = 0.1i`,
|
||||||
|
`0.1 + 0.9 = 1`,
|
||||||
|
`1e100 + 1e100 = 2e100`,
|
||||||
|
|
||||||
|
`0 - 0 = 0`,
|
||||||
|
`0 - 0.1 = -0.1`,
|
||||||
|
`0 - 0.1i = -0.1i`,
|
||||||
|
`1e100 - 1e100 = 0`,
|
||||||
|
|
||||||
|
`0 * 0 = 0`,
|
||||||
|
`1 * 0.1 = 0.1`,
|
||||||
|
`1 * 0.1i = 0.1i`,
|
||||||
|
`1i * 1i = -1`,
|
||||||
|
|
||||||
|
`0 / 0 = "division_by_zero"`,
|
||||||
|
`10 / 2 = 5`,
|
||||||
|
`5 / 3 = 5/3`,
|
||||||
|
|
||||||
|
`0 % 0 = "runtime_error:_integer_divide_by_zero"`, // TODO(gri) should be the same as for /
|
||||||
|
`10 % 3 = 1`,
|
||||||
|
// etc.
|
||||||
|
|
||||||
|
// shifts
|
||||||
|
`0 << 0 = 0`,
|
||||||
|
`1 << 10 = 1024`,
|
||||||
|
// etc.
|
||||||
|
|
||||||
|
// comparisons
|
||||||
|
`false == false = true`,
|
||||||
|
`false == true = false`,
|
||||||
|
`true == false = false`,
|
||||||
|
`true == true = true`,
|
||||||
|
|
||||||
|
`false != false = false`,
|
||||||
|
`false != true = true`,
|
||||||
|
`true != false = true`,
|
||||||
|
`true != true = false`,
|
||||||
|
|
||||||
|
`"foo" == "bar" = false`,
|
||||||
|
`"foo" != "bar" = true`,
|
||||||
|
`"foo" < "bar" = false`,
|
||||||
|
`"foo" <= "bar" = false`,
|
||||||
|
`"foo" > "bar" = true`,
|
||||||
|
`"foo" >= "bar" = true`,
|
||||||
|
|
||||||
|
`0 != 0 = false`,
|
||||||
|
|
||||||
|
// etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOps(t *testing.T) {
|
||||||
|
for _, test := range tests {
|
||||||
|
var got, want Value
|
||||||
|
|
||||||
|
switch a := strings.Split(test, " "); len(a) {
|
||||||
|
case 4:
|
||||||
|
got = doOp(nil, op[a[0]], val(a[1]))
|
||||||
|
want = val(a[3])
|
||||||
|
case 5:
|
||||||
|
got = doOp(val(a[0]), op[a[1]], val(a[2]))
|
||||||
|
want = val(a[4])
|
||||||
|
default:
|
||||||
|
t.Errorf("invalid test case: %s", test)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Compare(got, token.EQL, want) {
|
||||||
|
t.Errorf("%s failed: got %s; want %s", test, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Support functions
|
||||||
|
|
||||||
|
func val(lit string) Value {
|
||||||
|
if len(lit) == 0 {
|
||||||
|
return MakeUnknown()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch lit {
|
||||||
|
case "?":
|
||||||
|
return MakeUnknown()
|
||||||
|
case "nil":
|
||||||
|
return MakeNil()
|
||||||
|
case "true":
|
||||||
|
return MakeBool(true)
|
||||||
|
case "false":
|
||||||
|
return MakeBool(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
tok := token.FLOAT
|
||||||
|
switch first, last := lit[0], lit[len(lit)-1]; {
|
||||||
|
case first == '"' || first == '`':
|
||||||
|
tok = token.STRING
|
||||||
|
lit = strings.Replace(lit, "_", " ", -1)
|
||||||
|
case first == '\'':
|
||||||
|
tok = token.CHAR
|
||||||
|
case last == 'i':
|
||||||
|
tok = token.IMAG
|
||||||
|
}
|
||||||
|
|
||||||
|
return MakeFromLiteral(lit, tok)
|
||||||
|
}
|
||||||
|
|
||||||
|
var op = map[string]token.Token{
|
||||||
|
"!": token.NOT,
|
||||||
|
|
||||||
|
"+": token.ADD,
|
||||||
|
"-": token.SUB,
|
||||||
|
"*": token.MUL,
|
||||||
|
"/": token.QUO,
|
||||||
|
"%": token.REM,
|
||||||
|
|
||||||
|
"<<": token.SHL,
|
||||||
|
">>": token.SHR,
|
||||||
|
|
||||||
|
"==": token.EQL,
|
||||||
|
"!=": token.NEQ,
|
||||||
|
"<": token.LSS,
|
||||||
|
"<=": token.LEQ,
|
||||||
|
">": token.GTR,
|
||||||
|
">=": token.GEQ,
|
||||||
|
}
|
||||||
|
|
||||||
|
func panicHandler(v *Value) {
|
||||||
|
switch p := recover().(type) {
|
||||||
|
case nil:
|
||||||
|
// nothing to do
|
||||||
|
case string:
|
||||||
|
*v = MakeString(p)
|
||||||
|
case error:
|
||||||
|
*v = MakeString(p.Error())
|
||||||
|
default:
|
||||||
|
panic(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func doOp(x Value, op token.Token, y Value) (z Value) {
|
||||||
|
defer panicHandler(&z)
|
||||||
|
|
||||||
|
if x == nil {
|
||||||
|
return UnaryOp(op, y, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch op {
|
||||||
|
case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
|
||||||
|
return MakeBool(Compare(x, op, y))
|
||||||
|
case token.SHL, token.SHR:
|
||||||
|
s, _ := Int64Val(y)
|
||||||
|
return Shift(x, op, uint(s))
|
||||||
|
default:
|
||||||
|
return BinaryOp(x, op, y)
|
||||||
|
}
|
||||||
|
}
|
122
go/types/api.go
Normal file
122
go/types/api.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
// Copyright 2012 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 declares the data structures for representing
|
||||||
|
// Go types and implements typechecking of package files.
|
||||||
|
//
|
||||||
|
// WARNING: THE TYPES API IS SUBJECT TO CHANGE.
|
||||||
|
//
|
||||||
|
package types
|
||||||
|
|
||||||
|
// Most correct programs should now be accepted by the types package,
|
||||||
|
// but there are several known bugs which permit incorrect programs to
|
||||||
|
// pass without errors. Please do not file issues against these for now
|
||||||
|
// since they are known already:
|
||||||
|
//
|
||||||
|
// BUG(gri): Method sets are computed loosely and don't distinguish between ptr vs non-pointer receivers.
|
||||||
|
// BUG(gri): Method expressions and method values work accidentally and may not be fully checked.
|
||||||
|
// BUG(gri): Conversions of constants only change the type, not the value (e.g., int(1.1) is wrong).
|
||||||
|
// BUG(gri): Some built-ins don't check parameters fully, yet (e.g. append).
|
||||||
|
// BUG(gri): Use of labels is not checked.
|
||||||
|
// BUG(gri): Unused variables and imports are not reported.
|
||||||
|
// BUG(gri): Interface vs non-interface comparisons are not correctly implemented.
|
||||||
|
// BUG(gri): Switch statements don't check correct use of 'fallthrough'.
|
||||||
|
// BUG(gri): Switch statements don't check duplicate cases for all types for which it is required.
|
||||||
|
// BUG(gri): Some built-ins may not be callable if in statement-context.
|
||||||
|
// BUG(gri): Duplicate declarations in different files may not be reported.
|
||||||
|
// BUG(gri): The type-checker assumes that the input *ast.Files were created by go/parser.
|
||||||
|
|
||||||
|
// The API is still slightly in flux and the following changes are considered:
|
||||||
|
//
|
||||||
|
// API(gri): Provide accessors uniformly to all fields and do not export fields directly.
|
||||||
|
// API(gri): Provide scope information for all objects.
|
||||||
|
// API(gri): Provide position information for all objects.
|
||||||
|
// API(gri): The semantics of QualifiedIdent needs to be revisited.
|
||||||
|
// API(gri): The GcImporter should probably be in its own package - it is only one of possible importers.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Context specifies the supporting context for type checking.
|
||||||
|
// An empty Context is a ready-to-use default context.
|
||||||
|
type Context struct {
|
||||||
|
// If Error != nil, it is called with each error found
|
||||||
|
// during type checking. The error strings of errors with
|
||||||
|
// detailed position information are formatted as follows:
|
||||||
|
// filename:line:column: message
|
||||||
|
Error func(err error)
|
||||||
|
|
||||||
|
// If Ident != nil, it is called for each identifier id
|
||||||
|
// denoting an Object in the files provided to Check, and
|
||||||
|
// obj is the denoted object.
|
||||||
|
// Ident is not called for fields and methods in struct or
|
||||||
|
// interface types or composite literals, or for blank (_)
|
||||||
|
// or dot (.) identifiers in dot-imports.
|
||||||
|
// TODO(gri) Consider making Fields and Methods ordinary
|
||||||
|
// Objects - than we could lift this restriction.
|
||||||
|
Ident func(id *ast.Ident, obj Object)
|
||||||
|
|
||||||
|
// If Expr != nil, it is called exactly once for each expression x
|
||||||
|
// that is type-checked: typ is the expression type, and val is the
|
||||||
|
// value if x is constant, val is nil otherwise.
|
||||||
|
//
|
||||||
|
// If x is a literal value (constant, composite literal), typ is always
|
||||||
|
// the dynamic type of x (never an interface type). Otherwise, typ is x's
|
||||||
|
// static type (possibly an interface type).
|
||||||
|
// TODO(gri): Should this hold for all constant expressions?
|
||||||
|
Expr func(x ast.Expr, typ Type, val exact.Value)
|
||||||
|
|
||||||
|
// If Import != nil, it is called for each imported package.
|
||||||
|
// Otherwise, GcImporter is called.
|
||||||
|
Import Importer
|
||||||
|
|
||||||
|
// If Alignof != nil, it is called to determine the alignment
|
||||||
|
// of the given type. Otherwise DefaultAlignmentof is called.
|
||||||
|
// Alignof must implement the alignment guarantees required by
|
||||||
|
// the spec.
|
||||||
|
Alignof func(Type) int64
|
||||||
|
|
||||||
|
// If Offsetsof != nil, it is called to determine the offsets
|
||||||
|
// of the given struct fields, in bytes. Otherwise DefaultOffsetsof
|
||||||
|
// is called. Offsetsof must implement the offset guarantees
|
||||||
|
// required by the spec.
|
||||||
|
Offsetsof func(fields []*Field) []int64
|
||||||
|
|
||||||
|
// If Sizeof != nil, it is called to determine the size of the
|
||||||
|
// given type. Otherwise, DefaultSizeof is called. Sizeof must
|
||||||
|
// implement the size guarantees required by the spec.
|
||||||
|
Sizeof func(Type) int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Importer resolves import paths to Package objects.
|
||||||
|
// The imports map records the packages already imported,
|
||||||
|
// indexed by package id (canonical import path).
|
||||||
|
// An Importer must determine the canonical import path and
|
||||||
|
// check the map to see if it is already present in the imports map.
|
||||||
|
// If so, the Importer can return the map entry. Otherwise, the
|
||||||
|
// Importer should load the package data for the given path into
|
||||||
|
// a new *Package, record pkg in the imports map, and then
|
||||||
|
// return pkg.
|
||||||
|
type Importer func(imports map[string]*Package, path string) (pkg *Package, err error)
|
||||||
|
|
||||||
|
// Check resolves and typechecks a set of package files within the given
|
||||||
|
// context. It returns the package and the first error encountered, if
|
||||||
|
// any. If the context's Error handler is nil, Check terminates as soon
|
||||||
|
// as the first error is encountered; otherwise it continues until the
|
||||||
|
// entire package is checked. If there are errors, the package may be
|
||||||
|
// only partially type-checked, and the resulting package may be incomplete
|
||||||
|
// (missing objects, imports, etc.).
|
||||||
|
func (ctxt *Context) Check(fset *token.FileSet, files []*ast.File) (*Package, error) {
|
||||||
|
return check(ctxt, fset, files)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check is shorthand for ctxt.Check where ctxt is a default (empty) context.
|
||||||
|
func Check(fset *token.FileSet, files []*ast.File) (*Package, error) {
|
||||||
|
var ctxt Context
|
||||||
|
return ctxt.Check(fset, files)
|
||||||
|
}
|
463
go/types/builtins.go
Normal file
463
go/types/builtins.go
Normal file
@ -0,0 +1,463 @@
|
|||||||
|
// Copyright 2012 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 typechecking of builtin function calls.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(gri): Several built-ins are missing assignment checks. As a result,
|
||||||
|
// non-constant shift arguments may not be properly type-checked.
|
||||||
|
|
||||||
|
// builtin typechecks a built-in call. The built-in type is bin, and iota is the current
|
||||||
|
// value of iota or -1 if iota doesn't have a value in the current context. The result
|
||||||
|
// of the call is returned via x. If the call has type errors, the returned x is marked
|
||||||
|
// as invalid (x.mode == invalid).
|
||||||
|
//
|
||||||
|
func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota int) {
|
||||||
|
args := call.Args
|
||||||
|
id := bin.id
|
||||||
|
|
||||||
|
// declare before goto's
|
||||||
|
var arg0 ast.Expr // first argument, if present
|
||||||
|
|
||||||
|
// check argument count
|
||||||
|
n := len(args)
|
||||||
|
msg := ""
|
||||||
|
if n < bin.nargs {
|
||||||
|
msg = "not enough"
|
||||||
|
} else if !bin.isVariadic && n > bin.nargs {
|
||||||
|
msg = "too many"
|
||||||
|
}
|
||||||
|
if msg != "" {
|
||||||
|
check.invalidOp(call.Pos(), msg+" arguments for %s (expected %d, found %d)", call, bin.nargs, n)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// common case: evaluate first argument if present;
|
||||||
|
// if it is an expression, x has the expression value
|
||||||
|
if n > 0 {
|
||||||
|
arg0 = args[0]
|
||||||
|
switch id {
|
||||||
|
case _Make, _New, _Print, _Println, _Offsetof, _Trace:
|
||||||
|
// respective cases below do the work
|
||||||
|
default:
|
||||||
|
// argument must be an expression
|
||||||
|
check.expr(x, arg0, nil, iota)
|
||||||
|
if x.mode == invalid {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch id {
|
||||||
|
case _Append:
|
||||||
|
if _, ok := underlying(x.typ).(*Slice); !ok {
|
||||||
|
check.invalidArg(x.pos(), "%s is not a typed slice", x)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
resultTyp := x.typ
|
||||||
|
for _, arg := range args[1:] {
|
||||||
|
check.expr(x, arg, nil, iota)
|
||||||
|
if x.mode == invalid {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
// TODO(gri) check assignability
|
||||||
|
}
|
||||||
|
x.mode = value
|
||||||
|
x.typ = resultTyp
|
||||||
|
|
||||||
|
case _Cap, _Len:
|
||||||
|
mode := invalid
|
||||||
|
var val exact.Value
|
||||||
|
switch typ := implicitArrayDeref(underlying(x.typ)).(type) {
|
||||||
|
case *Basic:
|
||||||
|
if isString(typ) && id == _Len {
|
||||||
|
if x.mode == constant {
|
||||||
|
mode = constant
|
||||||
|
val = exact.MakeInt64(int64(len(exact.StringVal(x.val))))
|
||||||
|
} else {
|
||||||
|
mode = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Array:
|
||||||
|
mode = value
|
||||||
|
// spec: "The expressions len(s) and cap(s) are constants
|
||||||
|
// if the type of s is an array or pointer to an array and
|
||||||
|
// the expression s does not contain channel receives or
|
||||||
|
// function calls; in this case s is not evaluated."
|
||||||
|
if !check.containsCallsOrReceives(arg0) {
|
||||||
|
mode = constant
|
||||||
|
val = exact.MakeInt64(typ.Len)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Slice, *Chan:
|
||||||
|
mode = value
|
||||||
|
|
||||||
|
case *Map:
|
||||||
|
if id == _Len {
|
||||||
|
mode = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mode == invalid {
|
||||||
|
check.invalidArg(x.pos(), "%s for %s", x, bin.name)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
x.mode = mode
|
||||||
|
x.typ = Typ[Int]
|
||||||
|
x.val = val
|
||||||
|
|
||||||
|
case _Close:
|
||||||
|
ch, ok := underlying(x.typ).(*Chan)
|
||||||
|
if !ok {
|
||||||
|
check.invalidArg(x.pos(), "%s is not a channel", x)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
if ch.Dir&ast.SEND == 0 {
|
||||||
|
check.invalidArg(x.pos(), "%s must not be a receive-only channel", x)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
x.mode = novalue
|
||||||
|
|
||||||
|
case _Complex:
|
||||||
|
if !check.complexArg(x) {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
var y operand
|
||||||
|
check.expr(&y, args[1], nil, iota)
|
||||||
|
if y.mode == invalid {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
if !check.complexArg(&y) {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
check.convertUntyped(x, y.typ)
|
||||||
|
if x.mode == invalid {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
check.convertUntyped(&y, x.typ)
|
||||||
|
if y.mode == invalid {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if !IsIdentical(x.typ, y.typ) {
|
||||||
|
check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := underlying(x.typ).(*Basic)
|
||||||
|
if x.mode == constant && y.mode == constant {
|
||||||
|
x.val = exact.BinaryOp(x.val, token.ADD, exact.MakeImag(y.val))
|
||||||
|
} else {
|
||||||
|
x.mode = value
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typ.Kind {
|
||||||
|
case Float32:
|
||||||
|
x.typ = Typ[Complex64]
|
||||||
|
case Float64:
|
||||||
|
x.typ = Typ[Complex128]
|
||||||
|
case UntypedInt, UntypedRune, UntypedFloat:
|
||||||
|
if x.mode == constant {
|
||||||
|
typ = defaultType(typ).(*Basic)
|
||||||
|
x.typ = Typ[UntypedComplex]
|
||||||
|
} else {
|
||||||
|
// untyped but not constant; probably because one
|
||||||
|
// operand is a non-constant shift of untyped lhs
|
||||||
|
typ = Typ[Float64]
|
||||||
|
x.typ = Typ[Complex128]
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
check.invalidArg(x.pos(), "float32 or float64 arguments expected")
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.mode != constant {
|
||||||
|
// The arguments have now their final types, which at run-
|
||||||
|
// time will be materialized. Update the expression trees.
|
||||||
|
// If the current types are untyped, the materialized type
|
||||||
|
// is the respective default type.
|
||||||
|
// (If the result is constant, the arguments are never
|
||||||
|
// materialized and there is nothing to do.)
|
||||||
|
check.updateExprType(args[0], typ, true)
|
||||||
|
check.updateExprType(args[1], typ, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Copy:
|
||||||
|
var y operand
|
||||||
|
check.expr(&y, args[1], nil, iota)
|
||||||
|
if y.mode == invalid {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
var dst, src Type
|
||||||
|
if t, ok := underlying(x.typ).(*Slice); ok {
|
||||||
|
dst = t.Elt
|
||||||
|
}
|
||||||
|
switch t := underlying(y.typ).(type) {
|
||||||
|
case *Basic:
|
||||||
|
if isString(y.typ) {
|
||||||
|
src = Typ[Byte]
|
||||||
|
}
|
||||||
|
case *Slice:
|
||||||
|
src = t.Elt
|
||||||
|
}
|
||||||
|
|
||||||
|
if dst == nil || src == nil {
|
||||||
|
check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if !IsIdentical(dst, src) {
|
||||||
|
check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = value
|
||||||
|
x.typ = Typ[Int]
|
||||||
|
|
||||||
|
case _Delete:
|
||||||
|
m, ok := underlying(x.typ).(*Map)
|
||||||
|
if !ok {
|
||||||
|
check.invalidArg(x.pos(), "%s is not a map", x)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
check.expr(x, args[1], nil, iota)
|
||||||
|
if x.mode == invalid {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
if !x.isAssignable(check.ctxt, m.Key) {
|
||||||
|
check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.Key)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
x.mode = novalue
|
||||||
|
|
||||||
|
case _Imag, _Real:
|
||||||
|
if !isComplex(x.typ) {
|
||||||
|
check.invalidArg(x.pos(), "%s must be a complex number", x)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
if x.mode == constant {
|
||||||
|
if id == _Real {
|
||||||
|
x.val = exact.Real(x.val)
|
||||||
|
} else {
|
||||||
|
x.val = exact.Imag(x.val)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x.mode = value
|
||||||
|
}
|
||||||
|
k := Invalid
|
||||||
|
switch underlying(x.typ).(*Basic).Kind {
|
||||||
|
case Complex64:
|
||||||
|
k = Float32
|
||||||
|
case Complex128:
|
||||||
|
k = Float64
|
||||||
|
case UntypedComplex:
|
||||||
|
k = UntypedFloat
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
x.typ = Typ[k]
|
||||||
|
|
||||||
|
case _Make:
|
||||||
|
resultTyp := check.typ(arg0, false)
|
||||||
|
if resultTyp == Typ[Invalid] {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
var min int // minimum number of arguments
|
||||||
|
switch underlying(resultTyp).(type) {
|
||||||
|
case *Slice:
|
||||||
|
min = 2
|
||||||
|
case *Map, *Chan:
|
||||||
|
min = 1
|
||||||
|
default:
|
||||||
|
check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
if n := len(args); n < min || min+1 < n {
|
||||||
|
check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, n)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
var sizes []int64 // constant integer arguments, if any
|
||||||
|
for _, arg := range args[1:] {
|
||||||
|
if s, ok := check.index(arg, -1, iota); ok && s >= 0 {
|
||||||
|
sizes = append(sizes, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(sizes) == 2 && sizes[0] > sizes[1] {
|
||||||
|
check.invalidArg(args[1].Pos(), "length and capacity swapped")
|
||||||
|
// safe to continue
|
||||||
|
}
|
||||||
|
x.mode = variable
|
||||||
|
x.typ = resultTyp
|
||||||
|
|
||||||
|
case _New:
|
||||||
|
resultTyp := check.typ(arg0, false)
|
||||||
|
if resultTyp == Typ[Invalid] {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
x.mode = variable
|
||||||
|
x.typ = &Pointer{Base: resultTyp}
|
||||||
|
|
||||||
|
case _Panic:
|
||||||
|
x.mode = novalue
|
||||||
|
|
||||||
|
case _Print, _Println:
|
||||||
|
for _, arg := range args {
|
||||||
|
check.expr(x, arg, nil, -1)
|
||||||
|
if x.mode == invalid {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x.mode = novalue
|
||||||
|
|
||||||
|
case _Recover:
|
||||||
|
x.mode = value
|
||||||
|
x.typ = new(Interface)
|
||||||
|
|
||||||
|
case _Alignof:
|
||||||
|
x.mode = constant
|
||||||
|
x.val = exact.MakeInt64(check.ctxt.alignof(x.typ))
|
||||||
|
x.typ = Typ[Uintptr]
|
||||||
|
|
||||||
|
case _Offsetof:
|
||||||
|
arg, ok := unparen(arg0).(*ast.SelectorExpr)
|
||||||
|
if !ok {
|
||||||
|
check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
check.expr(x, arg.X, nil, -1)
|
||||||
|
if x.mode == invalid {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
sel := arg.Sel.Name
|
||||||
|
res := lookupField(x.typ, QualifiedName{check.pkg, arg.Sel.Name})
|
||||||
|
if res.index == nil {
|
||||||
|
check.invalidArg(x.pos(), "%s has no single field %s", x, sel)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
offs := check.ctxt.offsetof(deref(x.typ), res.index)
|
||||||
|
if offs < 0 {
|
||||||
|
check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, x)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
x.mode = constant
|
||||||
|
x.val = exact.MakeInt64(offs)
|
||||||
|
x.typ = Typ[Uintptr]
|
||||||
|
|
||||||
|
case _Sizeof:
|
||||||
|
x.mode = constant
|
||||||
|
x.val = exact.MakeInt64(check.ctxt.sizeof(x.typ))
|
||||||
|
x.typ = Typ[Uintptr]
|
||||||
|
|
||||||
|
case _Assert:
|
||||||
|
// assert(pred) causes a typechecker error if pred is false.
|
||||||
|
// The result of assert is the value of pred if there is no error.
|
||||||
|
// Note: assert is only available in self-test mode.
|
||||||
|
if x.mode != constant || !isBoolean(x.typ) {
|
||||||
|
check.invalidArg(x.pos(), "%s is not a boolean constant", x)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
if x.val.Kind() != exact.Bool {
|
||||||
|
check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
if !exact.BoolVal(x.val) {
|
||||||
|
check.errorf(call.Pos(), "%s failed", call)
|
||||||
|
// compile-time assertion failure - safe to continue
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Trace:
|
||||||
|
// trace(x, y, z, ...) dumps the positions, expressions, and
|
||||||
|
// values of its arguments. The result of trace is the value
|
||||||
|
// of the first argument.
|
||||||
|
// Note: trace is only available in self-test mode.
|
||||||
|
if len(args) == 0 {
|
||||||
|
check.dump("%s: trace() without arguments", call.Pos())
|
||||||
|
x.mode = novalue
|
||||||
|
x.expr = call
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var t operand
|
||||||
|
x1 := x
|
||||||
|
for _, arg := range args {
|
||||||
|
check.rawExpr(x1, arg, nil, iota, true) // permit trace for types, e.g.: new(trace(T))
|
||||||
|
check.dump("%s: %s", x1.pos(), x1)
|
||||||
|
x1 = &t // use incoming x only for first argument
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.invalidAST(call.Pos(), "unknown builtin id %d", id)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
x.expr = call
|
||||||
|
return
|
||||||
|
|
||||||
|
Error:
|
||||||
|
x.mode = invalid
|
||||||
|
x.expr = call
|
||||||
|
}
|
||||||
|
|
||||||
|
// implicitArrayDeref returns A if typ is of the form *A and A is an array;
|
||||||
|
// otherwise it returns typ.
|
||||||
|
//
|
||||||
|
func implicitArrayDeref(typ Type) Type {
|
||||||
|
if p, ok := typ.(*Pointer); ok {
|
||||||
|
if a, ok := underlying(p.Base).(*Array); ok {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// containsCallsOrReceives reports if x contains function calls or channel receives.
|
||||||
|
// Expects that x was type-checked already.
|
||||||
|
//
|
||||||
|
func (check *checker) containsCallsOrReceives(x ast.Expr) (found bool) {
|
||||||
|
ast.Inspect(x, func(x ast.Node) bool {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case *ast.CallExpr:
|
||||||
|
// calls and conversions look the same
|
||||||
|
if !check.conversions[x] {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
case *ast.UnaryExpr:
|
||||||
|
if x.Op == token.ARROW {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !found // no need to continue if found
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// unparen removes any parentheses surrounding an expression and returns
|
||||||
|
// the naked expression.
|
||||||
|
//
|
||||||
|
func unparen(x ast.Expr) ast.Expr {
|
||||||
|
if p, ok := x.(*ast.ParenExpr); ok {
|
||||||
|
return unparen(p.X)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) complexArg(x *operand) bool {
|
||||||
|
t, _ := underlying(x.typ).(*Basic)
|
||||||
|
if t != nil && (t.Info&IsFloat != 0 || t.Kind == UntypedInt || t.Kind == UntypedRune) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
check.invalidArg(x.pos(), "%s must be a float32, float64, or an untyped non-complex numeric constant", x)
|
||||||
|
return false
|
||||||
|
}
|
511
go/types/check.go
Normal file
511
go/types/check.go
Normal file
@ -0,0 +1,511 @@
|
|||||||
|
// Copyright 2011 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 the Check function, which typechecks a package.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
// debugging support
|
||||||
|
const (
|
||||||
|
debug = true // leave on during development
|
||||||
|
trace = false // turn on for detailed type resolution traces
|
||||||
|
)
|
||||||
|
|
||||||
|
// exprInfo stores type and constant value for an untyped expression.
|
||||||
|
type exprInfo struct {
|
||||||
|
isLhs bool // expression is lhs operand of a shift with delayed type check
|
||||||
|
typ *Basic
|
||||||
|
val exact.Value // constant value; or nil (if not a constant)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A checker is an instance of the type checker.
|
||||||
|
type checker struct {
|
||||||
|
ctxt *Context
|
||||||
|
fset *token.FileSet
|
||||||
|
files []*ast.File
|
||||||
|
|
||||||
|
// lazily initialized
|
||||||
|
pkg *Package // current package
|
||||||
|
firsterr error // first error encountered
|
||||||
|
idents map[*ast.Ident]Object // maps identifiers to their unique object
|
||||||
|
objects map[*ast.Object]Object // maps *ast.Objects to their unique object
|
||||||
|
initspecs map[*ast.ValueSpec]*ast.ValueSpec // "inherited" type and initialization expressions for constant declarations
|
||||||
|
methods map[*TypeName]*Scope // maps type names to associated methods
|
||||||
|
conversions map[*ast.CallExpr]bool // set of type-checked conversions (to distinguish from calls)
|
||||||
|
untyped map[ast.Expr]exprInfo // map of expressions without final type
|
||||||
|
|
||||||
|
// functions
|
||||||
|
funclist []function // list of functions/methods with correct signatures and non-empty bodies
|
||||||
|
funcsig *Signature // signature of currently typechecked function
|
||||||
|
pos []token.Pos // stack of expr positions; debugging support, used if trace is set
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) register(id *ast.Ident, obj Object) {
|
||||||
|
// When an expression is evaluated more than once (happens
|
||||||
|
// in rare cases, e.g. for statement expressions, see
|
||||||
|
// comment in stmt.go), the object has been registered
|
||||||
|
// before. Don't do anything in that case.
|
||||||
|
if alt := check.idents[id]; alt != nil {
|
||||||
|
assert(alt == obj)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check.idents[id] = obj
|
||||||
|
if f := check.ctxt.Ident; f != nil {
|
||||||
|
f(id, obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookup returns the unique Object denoted by the identifier.
|
||||||
|
// For identifiers without assigned *ast.Object, it uses the
|
||||||
|
// checker.idents map; for identifiers with an *ast.Object it
|
||||||
|
// uses the checker.objects map.
|
||||||
|
//
|
||||||
|
// TODO(gri) Once identifier resolution is done entirely by
|
||||||
|
// the typechecker, only the idents map is needed.
|
||||||
|
//
|
||||||
|
func (check *checker) lookup(ident *ast.Ident) Object {
|
||||||
|
obj := check.idents[ident]
|
||||||
|
astObj := ident.Obj
|
||||||
|
|
||||||
|
if obj != nil {
|
||||||
|
assert(astObj == nil || check.objects[astObj] == nil || check.objects[astObj] == obj)
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
if astObj == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj = check.objects[astObj]; obj == nil {
|
||||||
|
obj = newObj(check.pkg, astObj)
|
||||||
|
check.objects[astObj] = obj
|
||||||
|
}
|
||||||
|
check.register(ident, obj)
|
||||||
|
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
type function struct {
|
||||||
|
obj *Func // for debugging/tracing only
|
||||||
|
sig *Signature
|
||||||
|
body *ast.BlockStmt
|
||||||
|
}
|
||||||
|
|
||||||
|
// later adds a function with non-empty body to the list of functions
|
||||||
|
// that need to be processed after all package-level declarations
|
||||||
|
// are typechecked.
|
||||||
|
//
|
||||||
|
func (check *checker) later(f *Func, sig *Signature, body *ast.BlockStmt) {
|
||||||
|
// functions implemented elsewhere (say in assembly) have no body
|
||||||
|
if body != nil {
|
||||||
|
check.funclist = append(check.funclist, function{f, sig, body})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) declareIdent(scope *Scope, ident *ast.Ident, obj Object) {
|
||||||
|
assert(check.lookup(ident) == nil) // identifier already declared or resolved
|
||||||
|
check.register(ident, obj)
|
||||||
|
if ident.Name != "_" {
|
||||||
|
if alt := scope.Insert(obj); alt != nil {
|
||||||
|
prevDecl := ""
|
||||||
|
if pos := alt.GetPos(); pos.IsValid() {
|
||||||
|
prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos))
|
||||||
|
}
|
||||||
|
check.errorf(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spec *ast.ValueSpec, iota int) {
|
||||||
|
if len(lhs) == 0 {
|
||||||
|
check.invalidAST(pos, "missing lhs in declaration")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine type for all of lhs, if any
|
||||||
|
// (but only set it for the object we typecheck!)
|
||||||
|
var typ Type
|
||||||
|
if spec.Type != nil {
|
||||||
|
typ = check.typ(spec.Type, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// len(lhs) > 0
|
||||||
|
rhs := spec.Values
|
||||||
|
if len(lhs) == len(rhs) {
|
||||||
|
// check only lhs and rhs corresponding to obj
|
||||||
|
var l, r ast.Expr
|
||||||
|
for i, name := range lhs {
|
||||||
|
if check.lookup(name) == obj {
|
||||||
|
l = lhs[i]
|
||||||
|
r = rhs[i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(l != nil)
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *Const:
|
||||||
|
obj.Type = typ
|
||||||
|
case *Var:
|
||||||
|
obj.Type = typ
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
check.assign1to1(l, r, nil, true, iota)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// there must be a type or initialization expressions
|
||||||
|
if typ == nil && len(rhs) == 0 {
|
||||||
|
check.invalidAST(pos, "missing type or initialization expression")
|
||||||
|
typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have a type, mark all of lhs
|
||||||
|
if typ != nil {
|
||||||
|
for _, name := range lhs {
|
||||||
|
switch obj := check.lookup(name).(type) {
|
||||||
|
case *Const:
|
||||||
|
obj.Type = typ
|
||||||
|
case *Var:
|
||||||
|
obj.Type = typ
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check initial values, if any
|
||||||
|
if len(rhs) > 0 {
|
||||||
|
// TODO(gri) should try to avoid this conversion
|
||||||
|
lhx := make([]ast.Expr, len(lhs))
|
||||||
|
for i, e := range lhs {
|
||||||
|
lhx[i] = e
|
||||||
|
}
|
||||||
|
check.assignNtoM(lhx, rhs, true, iota)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// object typechecks an object by assigning it a type.
|
||||||
|
//
|
||||||
|
func (check *checker) object(obj Object, cycleOk bool) {
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *Package:
|
||||||
|
// nothing to do
|
||||||
|
|
||||||
|
case *Const:
|
||||||
|
if obj.Type != nil {
|
||||||
|
return // already checked
|
||||||
|
}
|
||||||
|
// The obj.Val field for constants is initialized to its respective
|
||||||
|
// iota value (type int) by the parser.
|
||||||
|
// If the object's type is Typ[Invalid], the object value is ignored.
|
||||||
|
// If the object's type is valid, the object value must be a legal
|
||||||
|
// constant value; it may be nil to indicate that we don't know the
|
||||||
|
// value of the constant (e.g., in: "const x = float32("foo")" we
|
||||||
|
// know that x is a constant and has type float32, but we don't
|
||||||
|
// have a value due to the error in the conversion).
|
||||||
|
if obj.visited {
|
||||||
|
check.errorf(obj.GetPos(), "illegal cycle in initialization of constant %s", obj.Name)
|
||||||
|
obj.Type = Typ[Invalid]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
obj.visited = true
|
||||||
|
spec := obj.spec
|
||||||
|
iota, ok := exact.Int64Val(obj.Val)
|
||||||
|
assert(ok)
|
||||||
|
obj.Val = exact.MakeUnknown()
|
||||||
|
// determine spec for type and initialization expressions
|
||||||
|
init := spec
|
||||||
|
if len(init.Values) == 0 {
|
||||||
|
init = check.initspecs[spec]
|
||||||
|
}
|
||||||
|
check.valueSpec(spec.Pos(), obj, spec.Names, init, int(iota))
|
||||||
|
|
||||||
|
case *Var:
|
||||||
|
if obj.Type != nil {
|
||||||
|
return // already checked
|
||||||
|
}
|
||||||
|
if obj.visited {
|
||||||
|
check.errorf(obj.GetPos(), "illegal cycle in initialization of variable %s", obj.Name)
|
||||||
|
obj.Type = Typ[Invalid]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
obj.visited = true
|
||||||
|
switch d := obj.decl.(type) {
|
||||||
|
case *ast.Field:
|
||||||
|
unreachable() // function parameters are always typed when collected
|
||||||
|
case *ast.ValueSpec:
|
||||||
|
check.valueSpec(d.Pos(), obj, d.Names, d, 0)
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
unreachable() // assign1to1 sets the type for failing short var decls
|
||||||
|
default:
|
||||||
|
unreachable() // see also function newObj
|
||||||
|
}
|
||||||
|
|
||||||
|
case *TypeName:
|
||||||
|
if obj.Type != nil {
|
||||||
|
return // already checked
|
||||||
|
}
|
||||||
|
typ := &NamedType{Obj: obj}
|
||||||
|
obj.Type = typ // "mark" object so recursion terminates
|
||||||
|
typ.Underlying = underlying(check.typ(obj.spec.Type, cycleOk))
|
||||||
|
// typecheck associated method signatures
|
||||||
|
if scope := check.methods[obj]; scope != nil {
|
||||||
|
switch t := typ.Underlying.(type) {
|
||||||
|
case *Struct:
|
||||||
|
// struct fields must not conflict with methods
|
||||||
|
for _, f := range t.Fields {
|
||||||
|
if m := scope.Lookup(f.Name); m != nil {
|
||||||
|
check.errorf(m.GetPos(), "type %s has both field and method named %s", obj.Name, f.Name)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *Interface:
|
||||||
|
// methods cannot be associated with an interface type
|
||||||
|
for _, m := range scope.Entries {
|
||||||
|
recv := m.(*Func).decl.Recv.List[0].Type
|
||||||
|
check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// typecheck method signatures
|
||||||
|
var methods []*Method
|
||||||
|
for _, obj := range scope.Entries {
|
||||||
|
m := obj.(*Func)
|
||||||
|
sig := check.typ(m.decl.Type, cycleOk).(*Signature)
|
||||||
|
params, _ := check.collectParams(m.decl.Recv, false)
|
||||||
|
sig.Recv = params[0] // the parser/assocMethod ensure there is exactly one parameter
|
||||||
|
m.Type = sig
|
||||||
|
methods = append(methods, &Method{QualifiedName{check.pkg, m.Name}, sig})
|
||||||
|
check.later(m, sig, m.decl.Body)
|
||||||
|
}
|
||||||
|
typ.Methods = methods
|
||||||
|
delete(check.methods, obj) // we don't need this scope anymore
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Func:
|
||||||
|
if obj.Type != nil {
|
||||||
|
return // already checked
|
||||||
|
}
|
||||||
|
fdecl := obj.decl
|
||||||
|
// methods are typechecked when their receivers are typechecked
|
||||||
|
if fdecl.Recv == nil {
|
||||||
|
sig := check.typ(fdecl.Type, cycleOk).(*Signature)
|
||||||
|
if obj.Name == "init" && (len(sig.Params) != 0 || len(sig.Results) != 0) {
|
||||||
|
check.errorf(fdecl.Pos(), "func init must have no arguments and no return values")
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
obj.Type = sig
|
||||||
|
check.later(obj, sig, fdecl.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assocInitvals associates "inherited" initialization expressions
|
||||||
|
// with the corresponding *ast.ValueSpec in the check.initspecs map
|
||||||
|
// for constant declarations without explicit initialization expressions.
|
||||||
|
//
|
||||||
|
func (check *checker) assocInitvals(decl *ast.GenDecl) {
|
||||||
|
var last *ast.ValueSpec
|
||||||
|
for _, s := range decl.Specs {
|
||||||
|
if s, ok := s.(*ast.ValueSpec); ok {
|
||||||
|
if len(s.Values) > 0 {
|
||||||
|
last = s
|
||||||
|
} else {
|
||||||
|
check.initspecs[s] = last
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if last == nil {
|
||||||
|
check.invalidAST(decl.Pos(), "no initialization values provided")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assocMethod associates a method declaration with the respective
|
||||||
|
// receiver base type. meth.Recv must exist.
|
||||||
|
//
|
||||||
|
func (check *checker) assocMethod(meth *ast.FuncDecl) {
|
||||||
|
// The receiver type is one of the following (enforced by parser):
|
||||||
|
// - *ast.Ident
|
||||||
|
// - *ast.StarExpr{*ast.Ident}
|
||||||
|
// - *ast.BadExpr (parser error)
|
||||||
|
typ := meth.Recv.List[0].Type
|
||||||
|
if ptr, ok := typ.(*ast.StarExpr); ok {
|
||||||
|
typ = ptr.X
|
||||||
|
}
|
||||||
|
// determine receiver base type name
|
||||||
|
ident, ok := typ.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
// not an identifier - parser reported error already
|
||||||
|
return // ignore this method
|
||||||
|
}
|
||||||
|
// determine receiver base type object
|
||||||
|
var tname *TypeName
|
||||||
|
if obj := check.lookup(ident); obj != nil {
|
||||||
|
obj, ok := obj.(*TypeName)
|
||||||
|
if !ok {
|
||||||
|
check.errorf(ident.Pos(), "%s is not a type", ident.Name)
|
||||||
|
return // ignore this method
|
||||||
|
}
|
||||||
|
if obj.spec == nil {
|
||||||
|
check.errorf(ident.Pos(), "cannot define method on non-local type %s", ident.Name)
|
||||||
|
return // ignore this method
|
||||||
|
}
|
||||||
|
tname = obj
|
||||||
|
} else {
|
||||||
|
// identifier not declared/resolved - parser reported error already
|
||||||
|
return // ignore this method
|
||||||
|
}
|
||||||
|
// declare method in receiver base type scope
|
||||||
|
scope := check.methods[tname]
|
||||||
|
if scope == nil {
|
||||||
|
scope = new(Scope)
|
||||||
|
check.methods[tname] = scope
|
||||||
|
}
|
||||||
|
check.declareIdent(scope, meth.Name, &Func{Pkg: check.pkg, Name: meth.Name.Name, decl: meth})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) decl(decl ast.Decl) {
|
||||||
|
switch d := decl.(type) {
|
||||||
|
case *ast.BadDecl:
|
||||||
|
// ignore
|
||||||
|
case *ast.GenDecl:
|
||||||
|
for _, spec := range d.Specs {
|
||||||
|
switch s := spec.(type) {
|
||||||
|
case *ast.ImportSpec:
|
||||||
|
// nothing to do (handled by check.resolve)
|
||||||
|
case *ast.ValueSpec:
|
||||||
|
for _, name := range s.Names {
|
||||||
|
check.object(check.lookup(name), false)
|
||||||
|
}
|
||||||
|
case *ast.TypeSpec:
|
||||||
|
check.object(check.lookup(s.Name), false)
|
||||||
|
default:
|
||||||
|
check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
// methods are checked when their respective base types are checked
|
||||||
|
if d.Recv != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
obj := check.lookup(d.Name)
|
||||||
|
// Initialization functions don't have an object associated with them
|
||||||
|
// since they are not in any scope. Create a dummy object for them.
|
||||||
|
if d.Name.Name == "init" {
|
||||||
|
assert(obj == nil) // all other functions should have an object
|
||||||
|
obj = &Func{Pkg: check.pkg, Name: d.Name.Name, decl: d}
|
||||||
|
check.register(d.Name, obj)
|
||||||
|
}
|
||||||
|
check.object(obj, false)
|
||||||
|
default:
|
||||||
|
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A bailout panic is raised to indicate early termination.
|
||||||
|
type bailout struct{}
|
||||||
|
|
||||||
|
func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package, err error) {
|
||||||
|
// initialize checker
|
||||||
|
check := checker{
|
||||||
|
ctxt: ctxt,
|
||||||
|
fset: fset,
|
||||||
|
files: files,
|
||||||
|
idents: make(map[*ast.Ident]Object),
|
||||||
|
objects: make(map[*ast.Object]Object),
|
||||||
|
initspecs: make(map[*ast.ValueSpec]*ast.ValueSpec),
|
||||||
|
methods: make(map[*TypeName]*Scope),
|
||||||
|
conversions: make(map[*ast.CallExpr]bool),
|
||||||
|
untyped: make(map[ast.Expr]exprInfo),
|
||||||
|
}
|
||||||
|
|
||||||
|
// set results and handle panics
|
||||||
|
defer func() {
|
||||||
|
pkg = check.pkg
|
||||||
|
switch p := recover().(type) {
|
||||||
|
case nil, bailout:
|
||||||
|
// normal return or early exit
|
||||||
|
err = check.firsterr
|
||||||
|
default:
|
||||||
|
// unexpected panic: don't crash clients
|
||||||
|
if debug {
|
||||||
|
check.dump("INTERNAL PANIC: %v", p)
|
||||||
|
panic(p)
|
||||||
|
}
|
||||||
|
// TODO(gri) add a test case for this scenario
|
||||||
|
err = fmt.Errorf("types internal error: %v", p)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// resolve identifiers
|
||||||
|
imp := ctxt.Import
|
||||||
|
if imp == nil {
|
||||||
|
imp = GcImport
|
||||||
|
}
|
||||||
|
methods := check.resolve(imp)
|
||||||
|
|
||||||
|
// associate methods with types
|
||||||
|
for _, m := range methods {
|
||||||
|
check.assocMethod(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// typecheck all declarations
|
||||||
|
for _, f := range check.files {
|
||||||
|
for _, d := range f.Decls {
|
||||||
|
check.decl(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// typecheck all function/method bodies
|
||||||
|
// (funclist may grow when checking statements - do not use range clause!)
|
||||||
|
for i := 0; i < len(check.funclist); i++ {
|
||||||
|
f := check.funclist[i]
|
||||||
|
if trace {
|
||||||
|
s := "<function literal>"
|
||||||
|
if f.obj != nil {
|
||||||
|
s = f.obj.Name
|
||||||
|
}
|
||||||
|
fmt.Println("---", s)
|
||||||
|
}
|
||||||
|
check.funcsig = f.sig
|
||||||
|
check.stmtList(f.body.List)
|
||||||
|
if len(f.sig.Results) > 0 && f.body != nil && !check.isTerminating(f.body, "") {
|
||||||
|
check.errorf(f.body.Rbrace, "missing return")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remaining untyped expressions must indeed be untyped
|
||||||
|
if debug {
|
||||||
|
for x, info := range check.untyped {
|
||||||
|
if !isUntyped(info.typ) {
|
||||||
|
check.dump("%s: %s (type %s) is not untyped", x.Pos(), x, info.typ)
|
||||||
|
panic(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// notify client of any untyped types left
|
||||||
|
// TODO(gri) Consider doing this before and
|
||||||
|
// after function body checking for smaller
|
||||||
|
// map size and more immediate feedback.
|
||||||
|
if ctxt.Expr != nil {
|
||||||
|
for x, info := range check.untyped {
|
||||||
|
ctxt.Expr(x, info.typ, info.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
244
go/types/check_test.go
Normal file
244
go/types/check_test.go
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
// Copyright 2011 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 a typechecker test harness. The packages specified
|
||||||
|
// in tests are typechecked. Error messages reported by the typechecker are
|
||||||
|
// compared against the error messages expected in the test files.
|
||||||
|
//
|
||||||
|
// Expected errors are indicated in the test files by putting a comment
|
||||||
|
// of the form /* ERROR "rx" */ immediately following an offending token.
|
||||||
|
// The harness will verify that an error matching the regular expression
|
||||||
|
// rx is reported at that source position. Consecutive comments may be
|
||||||
|
// used to indicate multiple errors for the same token position.
|
||||||
|
//
|
||||||
|
// For instance, the following test file indicates that a "not declared"
|
||||||
|
// error should be reported for the undeclared variable x:
|
||||||
|
//
|
||||||
|
// package p
|
||||||
|
// func f() {
|
||||||
|
// _ = x /* ERROR "not declared" */ + 1
|
||||||
|
// }
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/scanner"
|
||||||
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var listErrors = flag.Bool("list", false, "list errors")
|
||||||
|
|
||||||
|
// The test filenames do not end in .go so that they are invisible
|
||||||
|
// to gofmt since they contain comments that must not change their
|
||||||
|
// positions relative to surrounding tokens.
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
files []string
|
||||||
|
}{
|
||||||
|
{"decls0", []string{"testdata/decls0.src"}},
|
||||||
|
{"decls1", []string{"testdata/decls1.src"}},
|
||||||
|
{"decls2", []string{"testdata/decls2a.src", "testdata/decls2b.src"}},
|
||||||
|
{"decls3", []string{"testdata/decls3.src"}},
|
||||||
|
{"const0", []string{"testdata/const0.src"}},
|
||||||
|
{"expr0", []string{"testdata/expr0.src"}},
|
||||||
|
{"expr1", []string{"testdata/expr1.src"}},
|
||||||
|
{"expr2", []string{"testdata/expr2.src"}},
|
||||||
|
{"expr3", []string{"testdata/expr3.src"}},
|
||||||
|
{"shifts", []string{"testdata/shifts.src"}},
|
||||||
|
{"builtins", []string{"testdata/builtins.src"}},
|
||||||
|
{"conversions", []string{"testdata/conversions.src"}},
|
||||||
|
{"stmt0", []string{"testdata/stmt0.src"}},
|
||||||
|
{"stmt1", []string{"testdata/stmt1.src"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
var fset = token.NewFileSet()
|
||||||
|
|
||||||
|
// Positioned errors are of the form filename:line:column: message .
|
||||||
|
var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(.*)`)
|
||||||
|
|
||||||
|
// splitError splits an error's error message into a position string
|
||||||
|
// and the actual error message. If there's no position information,
|
||||||
|
// pos is the empty string, and msg is the entire error message.
|
||||||
|
//
|
||||||
|
func splitError(err error) (pos, msg string) {
|
||||||
|
msg = err.Error()
|
||||||
|
if m := posMsgRx.FindStringSubmatch(msg); len(m) == 3 {
|
||||||
|
pos = m[1]
|
||||||
|
msg = m[2]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFiles(t *testing.T, testname string, filenames []string) ([]*ast.File, []error) {
|
||||||
|
var files []*ast.File
|
||||||
|
var errlist []error
|
||||||
|
for _, filename := range filenames {
|
||||||
|
file, err := parser.ParseFile(fset, filename, nil, parser.DeclarationErrors|parser.AllErrors)
|
||||||
|
if file == nil {
|
||||||
|
t.Fatalf("%s: could not parse file %s", testname, filename)
|
||||||
|
}
|
||||||
|
files = append(files, file)
|
||||||
|
if err != nil {
|
||||||
|
if list, _ := err.(scanner.ErrorList); len(list) > 0 {
|
||||||
|
for _, err := range list {
|
||||||
|
errlist = append(errlist, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errlist = append(errlist, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files, errlist
|
||||||
|
}
|
||||||
|
|
||||||
|
// ERROR comments must be of the form /* ERROR "rx" */ and rx is
|
||||||
|
// a regular expression that matches the expected error message.
|
||||||
|
//
|
||||||
|
var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`)
|
||||||
|
|
||||||
|
// errMap collects the regular expressions of ERROR comments found
|
||||||
|
// in files and returns them as a map of error positions to error messages.
|
||||||
|
//
|
||||||
|
func errMap(t *testing.T, testname string, files []*ast.File) map[string][]string {
|
||||||
|
// map of position strings to lists of error message patterns
|
||||||
|
errmap := make(map[string][]string)
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
filename := fset.Position(file.Package).Filename
|
||||||
|
src, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s: could not read %s", testname, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
var s scanner.Scanner
|
||||||
|
s.Init(fset.AddFile(filename, fset.Base(), len(src)), src, nil, scanner.ScanComments)
|
||||||
|
var prev string // position string of last non-comment, non-semicolon token
|
||||||
|
|
||||||
|
scanFile:
|
||||||
|
for {
|
||||||
|
pos, tok, lit := s.Scan()
|
||||||
|
switch tok {
|
||||||
|
case token.EOF:
|
||||||
|
break scanFile
|
||||||
|
case token.COMMENT:
|
||||||
|
if s := errRx.FindStringSubmatch(lit); len(s) == 2 {
|
||||||
|
errmap[prev] = append(errmap[prev], s[1])
|
||||||
|
}
|
||||||
|
case token.SEMICOLON:
|
||||||
|
// ignore automatically inserted semicolon
|
||||||
|
if lit == "\n" {
|
||||||
|
continue scanFile
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
prev = fset.Position(pos).String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errmap
|
||||||
|
}
|
||||||
|
|
||||||
|
func eliminate(t *testing.T, errmap map[string][]string, errlist []error) {
|
||||||
|
for _, err := range errlist {
|
||||||
|
pos, gotMsg := splitError(err)
|
||||||
|
list := errmap[pos]
|
||||||
|
index := -1 // list index of matching message, if any
|
||||||
|
// we expect one of the messages in list to match the error at pos
|
||||||
|
for i, wantRx := range list {
|
||||||
|
rx, err := regexp.Compile(wantRx)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: %v", pos, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if rx.MatchString(gotMsg) {
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if index >= 0 {
|
||||||
|
// eliminate from list
|
||||||
|
if n := len(list) - 1; n > 0 {
|
||||||
|
// not the last entry - swap in last element and shorten list by 1
|
||||||
|
list[index] = list[n]
|
||||||
|
errmap[pos] = list[:n]
|
||||||
|
} else {
|
||||||
|
// last entry - remove list from map
|
||||||
|
delete(errmap, pos)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("%s: no error expected: %q", pos, gotMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkFiles(t *testing.T, testname string, testfiles []string) {
|
||||||
|
// parse files and collect parser errors
|
||||||
|
files, errlist := parseFiles(t, testname, testfiles)
|
||||||
|
|
||||||
|
// typecheck and collect typechecker errors
|
||||||
|
var ctxt Context
|
||||||
|
ctxt.Error = func(err error) { errlist = append(errlist, err) }
|
||||||
|
ctxt.Check(fset, files)
|
||||||
|
|
||||||
|
if *listErrors {
|
||||||
|
t.Errorf("--- %s: %d errors found:", testname, len(errlist))
|
||||||
|
for _, err := range errlist {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// match and eliminate errors;
|
||||||
|
// we are expecting the following errors
|
||||||
|
errmap := errMap(t, testname, files)
|
||||||
|
eliminate(t, errmap, errlist)
|
||||||
|
|
||||||
|
// there should be no expected errors left
|
||||||
|
if len(errmap) > 0 {
|
||||||
|
t.Errorf("--- %s: %d source positions with expected (but not reported) errors:", testname, len(errmap))
|
||||||
|
for pos, list := range errmap {
|
||||||
|
for _, rx := range list {
|
||||||
|
t.Errorf("%s: %q", pos, rx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testBuiltinsDeclared = false
|
||||||
|
|
||||||
|
func TestCheck(t *testing.T) {
|
||||||
|
// Declare builtins for testing.
|
||||||
|
// Not done in an init func to avoid an init race with
|
||||||
|
// the construction of the Universe var.
|
||||||
|
if !testBuiltinsDeclared {
|
||||||
|
testBuiltinsDeclared = true
|
||||||
|
// Pkg == nil for Universe objects
|
||||||
|
def(&Func{Name: "assert", Type: &builtin{_Assert, "assert", 1, false, true}})
|
||||||
|
def(&Func{Name: "trace", Type: &builtin{_Trace, "trace", 0, true, true}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// For easy debugging w/o changing the testing code,
|
||||||
|
// if there is a local test file, only test that file.
|
||||||
|
const testfile = "testdata/test.go"
|
||||||
|
if fi, err := os.Stat(testfile); err == nil && !fi.IsDir() {
|
||||||
|
fmt.Printf("WARNING: Testing only %s (remove it to run all tests)\n", testfile)
|
||||||
|
checkFiles(t, testfile, []string{testfile})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, run all the tests.
|
||||||
|
for _, test := range tests {
|
||||||
|
checkFiles(t, test.name, test.files)
|
||||||
|
}
|
||||||
|
}
|
158
go/types/conversions.go
Normal file
158
go/types/conversions.go
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
// Copyright 2012 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 typechecking of conversions.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
// conversion typechecks the type conversion conv to type typ. iota is the current
|
||||||
|
// value of iota or -1 if iota doesn't have a value in the current context. The result
|
||||||
|
// of the conversion is returned via x. If the conversion has type errors, the returned
|
||||||
|
// x is marked as invalid (x.mode == invalid).
|
||||||
|
//
|
||||||
|
func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota int) {
|
||||||
|
// all conversions have one argument
|
||||||
|
if len(conv.Args) != 1 {
|
||||||
|
check.invalidOp(conv.Pos(), "%s conversion requires exactly one argument", conv)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluate argument
|
||||||
|
check.expr(x, conv.Args[0], nil, iota)
|
||||||
|
if x.mode == invalid {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.mode == constant && isConstType(typ) {
|
||||||
|
// constant conversion
|
||||||
|
typ := underlying(typ).(*Basic)
|
||||||
|
// For now just implement string(x) where x is an integer,
|
||||||
|
// as a temporary work-around for issue 4982, which is a
|
||||||
|
// common issue.
|
||||||
|
if typ.Kind == String {
|
||||||
|
switch {
|
||||||
|
case x.isInteger():
|
||||||
|
codepoint := int64(-1)
|
||||||
|
if i, ok := exact.Int64Val(x.val); ok {
|
||||||
|
codepoint = i
|
||||||
|
}
|
||||||
|
// If codepoint < 0 the absolute value is too large (or unknown) for
|
||||||
|
// conversion. This is the same as converting any other out-of-range
|
||||||
|
// value - let string(codepoint) do the work.
|
||||||
|
x.val = exact.MakeString(string(codepoint))
|
||||||
|
case isString(x.typ):
|
||||||
|
// nothing to do
|
||||||
|
default:
|
||||||
|
goto ErrorMsg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO(gri) verify the remaining conversions.
|
||||||
|
} else {
|
||||||
|
// non-constant conversion
|
||||||
|
if !x.isConvertible(check.ctxt, typ) {
|
||||||
|
goto ErrorMsg
|
||||||
|
}
|
||||||
|
x.mode = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// the conversion argument types are final; for now we just use x.typ
|
||||||
|
// TODO(gri) Fix this. For untyped constants, the type should be typ.
|
||||||
|
check.updateExprType(x.expr, x.typ, true)
|
||||||
|
|
||||||
|
check.conversions[conv] = true // for cap/len checking
|
||||||
|
x.expr = conv
|
||||||
|
x.typ = typ
|
||||||
|
return
|
||||||
|
|
||||||
|
ErrorMsg:
|
||||||
|
check.invalidOp(conv.Pos(), "cannot convert %s to %s", x, typ)
|
||||||
|
Error:
|
||||||
|
x.mode = invalid
|
||||||
|
x.expr = conv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *operand) isConvertible(ctxt *Context, T Type) bool {
|
||||||
|
// "x is assignable to T"
|
||||||
|
if x.isAssignable(ctxt, T) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x's type and T have identical underlying types"
|
||||||
|
V := x.typ
|
||||||
|
Vu := underlying(V)
|
||||||
|
Tu := underlying(T)
|
||||||
|
if IsIdentical(Vu, Tu) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x's type and T are unnamed pointer types and their pointer base types have identical underlying types"
|
||||||
|
if V, ok := V.(*Pointer); ok {
|
||||||
|
if T, ok := T.(*Pointer); ok {
|
||||||
|
if IsIdentical(underlying(V.Base), underlying(T.Base)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x's type and T are both integer or floating point types"
|
||||||
|
if (isInteger(V) || isFloat(V)) && (isInteger(T) || isFloat(T)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x's type and T are both complex types"
|
||||||
|
if isComplex(V) && isComplex(T) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x is an integer or a slice of bytes or runes and T is a string type"
|
||||||
|
if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x is a string and T is a slice of bytes or runes"
|
||||||
|
if isString(V) && isBytesOrRunes(Tu) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// package unsafe:
|
||||||
|
// "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
|
||||||
|
if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// "and vice versa"
|
||||||
|
if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUintptr(typ Type) bool {
|
||||||
|
t, ok := typ.(*Basic)
|
||||||
|
return ok && t.Kind == Uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUnsafePointer(typ Type) bool {
|
||||||
|
t, ok := typ.(*Basic)
|
||||||
|
return ok && t.Kind == UnsafePointer
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPointer(typ Type) bool {
|
||||||
|
_, ok := typ.(*Pointer)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBytesOrRunes(typ Type) bool {
|
||||||
|
if s, ok := typ.(*Slice); ok {
|
||||||
|
t, ok := underlying(s.Elt).(*Basic)
|
||||||
|
return ok && (t.Kind == Byte || t.Kind == Rune)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
335
go/types/errors.go
Normal file
335
go/types/errors.go
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
// Copyright 2012 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 various error reporters.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(gri) eventually assert and unimplemented should disappear.
|
||||||
|
func assert(p bool) {
|
||||||
|
if !p {
|
||||||
|
panic("assertion failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unreachable() {
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) printTrace(format string, args []interface{}) {
|
||||||
|
const dots = ". . . . . . . . . . . . . . . . . . . . "
|
||||||
|
n := len(check.pos) - 1
|
||||||
|
i := 3 * n
|
||||||
|
for i > len(dots) {
|
||||||
|
fmt.Print(dots)
|
||||||
|
i -= len(dots)
|
||||||
|
}
|
||||||
|
// i <= len(dots)
|
||||||
|
fmt.Printf("%s:\t", check.fset.Position(check.pos[n]))
|
||||||
|
fmt.Print(dots[0:i])
|
||||||
|
fmt.Println(check.formatMsg(format, args))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) trace(pos token.Pos, format string, args ...interface{}) {
|
||||||
|
check.pos = append(check.pos, pos)
|
||||||
|
check.printTrace(format, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) untrace(format string, args ...interface{}) {
|
||||||
|
if len(format) > 0 {
|
||||||
|
check.printTrace(format, args)
|
||||||
|
}
|
||||||
|
check.pos = check.pos[:len(check.pos)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) formatMsg(format string, args []interface{}) string {
|
||||||
|
for i, arg := range args {
|
||||||
|
switch a := arg.(type) {
|
||||||
|
case token.Pos:
|
||||||
|
args[i] = check.fset.Position(a).String()
|
||||||
|
case ast.Expr:
|
||||||
|
args[i] = exprString(a)
|
||||||
|
case Type:
|
||||||
|
args[i] = typeString(a)
|
||||||
|
case operand:
|
||||||
|
panic("internal error: should always pass *operand")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// dump is only needed for debugging
|
||||||
|
func (check *checker) dump(format string, args ...interface{}) {
|
||||||
|
fmt.Println(check.formatMsg(format, args))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) err(err error) {
|
||||||
|
if check.firsterr == nil {
|
||||||
|
check.firsterr = err
|
||||||
|
}
|
||||||
|
f := check.ctxt.Error
|
||||||
|
if f == nil {
|
||||||
|
panic(bailout{}) // report only first error
|
||||||
|
}
|
||||||
|
f(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) errorf(pos token.Pos, format string, args ...interface{}) {
|
||||||
|
check.err(fmt.Errorf("%s: %s", check.fset.Position(pos), check.formatMsg(format, args)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) invalidAST(pos token.Pos, format string, args ...interface{}) {
|
||||||
|
check.errorf(pos, "invalid AST: "+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) invalidArg(pos token.Pos, format string, args ...interface{}) {
|
||||||
|
check.errorf(pos, "invalid argument: "+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) invalidOp(pos token.Pos, format string, args ...interface{}) {
|
||||||
|
check.errorf(pos, "invalid operation: "+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// exprString returns a (simplified) string representation for an expression.
|
||||||
|
func exprString(expr ast.Expr) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
writeExpr(&buf, expr)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) Need to merge with typeString since some expressions are types (try: ([]int)(a))
|
||||||
|
func writeExpr(buf *bytes.Buffer, expr ast.Expr) {
|
||||||
|
switch x := expr.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
buf.WriteString(x.Name)
|
||||||
|
|
||||||
|
case *ast.BasicLit:
|
||||||
|
buf.WriteString(x.Value)
|
||||||
|
|
||||||
|
case *ast.FuncLit:
|
||||||
|
buf.WriteString("(func literal)")
|
||||||
|
|
||||||
|
case *ast.CompositeLit:
|
||||||
|
buf.WriteString("(composite literal)")
|
||||||
|
|
||||||
|
case *ast.ParenExpr:
|
||||||
|
buf.WriteByte('(')
|
||||||
|
writeExpr(buf, x.X)
|
||||||
|
buf.WriteByte(')')
|
||||||
|
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
writeExpr(buf, x.X)
|
||||||
|
buf.WriteByte('.')
|
||||||
|
buf.WriteString(x.Sel.Name)
|
||||||
|
|
||||||
|
case *ast.IndexExpr:
|
||||||
|
writeExpr(buf, x.X)
|
||||||
|
buf.WriteByte('[')
|
||||||
|
writeExpr(buf, x.Index)
|
||||||
|
buf.WriteByte(']')
|
||||||
|
|
||||||
|
case *ast.SliceExpr:
|
||||||
|
writeExpr(buf, x.X)
|
||||||
|
buf.WriteByte('[')
|
||||||
|
if x.Low != nil {
|
||||||
|
writeExpr(buf, x.Low)
|
||||||
|
}
|
||||||
|
buf.WriteByte(':')
|
||||||
|
if x.High != nil {
|
||||||
|
writeExpr(buf, x.High)
|
||||||
|
}
|
||||||
|
buf.WriteByte(']')
|
||||||
|
|
||||||
|
case *ast.TypeAssertExpr:
|
||||||
|
writeExpr(buf, x.X)
|
||||||
|
buf.WriteString(".(...)")
|
||||||
|
|
||||||
|
case *ast.CallExpr:
|
||||||
|
writeExpr(buf, x.Fun)
|
||||||
|
buf.WriteByte('(')
|
||||||
|
for i, arg := range x.Args {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
writeExpr(buf, arg)
|
||||||
|
}
|
||||||
|
buf.WriteByte(')')
|
||||||
|
|
||||||
|
case *ast.StarExpr:
|
||||||
|
buf.WriteByte('*')
|
||||||
|
writeExpr(buf, x.X)
|
||||||
|
|
||||||
|
case *ast.UnaryExpr:
|
||||||
|
buf.WriteString(x.Op.String())
|
||||||
|
writeExpr(buf, x.X)
|
||||||
|
|
||||||
|
case *ast.BinaryExpr:
|
||||||
|
// The AST preserves source-level parentheses so there is
|
||||||
|
// no need to introduce parentheses here for correctness.
|
||||||
|
writeExpr(buf, x.X)
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
buf.WriteString(x.Op.String())
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
writeExpr(buf, x.Y)
|
||||||
|
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(buf, "<expr %T>", x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeString returns a string representation for typ.
|
||||||
|
func typeString(typ Type) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
writeType(&buf, typ)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeParams(buf *bytes.Buffer, params []*Var, isVariadic bool) {
|
||||||
|
buf.WriteByte('(')
|
||||||
|
for i, par := range params {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
if par.Name != "" {
|
||||||
|
buf.WriteString(par.Name)
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
}
|
||||||
|
if isVariadic && i == len(params)-1 {
|
||||||
|
buf.WriteString("...")
|
||||||
|
}
|
||||||
|
writeType(buf, par.Type)
|
||||||
|
}
|
||||||
|
buf.WriteByte(')')
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeSignature(buf *bytes.Buffer, sig *Signature) {
|
||||||
|
writeParams(buf, sig.Params, sig.IsVariadic)
|
||||||
|
if len(sig.Results) == 0 {
|
||||||
|
// no result
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
if len(sig.Results) == 1 && sig.Results[0].Name == "" {
|
||||||
|
// single unnamed result
|
||||||
|
writeType(buf, sig.Results[0].Type.(Type))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiple or named result(s)
|
||||||
|
writeParams(buf, sig.Results, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeType(buf *bytes.Buffer, typ Type) {
|
||||||
|
switch t := typ.(type) {
|
||||||
|
case nil:
|
||||||
|
buf.WriteString("<nil>")
|
||||||
|
|
||||||
|
case *Basic:
|
||||||
|
buf.WriteString(t.Name)
|
||||||
|
|
||||||
|
case *Array:
|
||||||
|
fmt.Fprintf(buf, "[%d]", t.Len)
|
||||||
|
writeType(buf, t.Elt)
|
||||||
|
|
||||||
|
case *Slice:
|
||||||
|
buf.WriteString("[]")
|
||||||
|
writeType(buf, t.Elt)
|
||||||
|
|
||||||
|
case *Struct:
|
||||||
|
buf.WriteString("struct{")
|
||||||
|
for i, f := range t.Fields {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString("; ")
|
||||||
|
}
|
||||||
|
if !f.IsAnonymous {
|
||||||
|
buf.WriteString(f.Name)
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
}
|
||||||
|
writeType(buf, f.Type)
|
||||||
|
if f.Tag != "" {
|
||||||
|
fmt.Fprintf(buf, " %q", f.Tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteByte('}')
|
||||||
|
|
||||||
|
case *Pointer:
|
||||||
|
buf.WriteByte('*')
|
||||||
|
writeType(buf, t.Base)
|
||||||
|
|
||||||
|
case *Result:
|
||||||
|
writeParams(buf, t.Values, false)
|
||||||
|
|
||||||
|
case *Signature:
|
||||||
|
buf.WriteString("func")
|
||||||
|
writeSignature(buf, t)
|
||||||
|
|
||||||
|
case *builtin:
|
||||||
|
fmt.Fprintf(buf, "<type of %s>", t.name)
|
||||||
|
|
||||||
|
case *Interface:
|
||||||
|
buf.WriteString("interface{")
|
||||||
|
for i, m := range t.Methods {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString("; ")
|
||||||
|
}
|
||||||
|
buf.WriteString(m.Name)
|
||||||
|
writeSignature(buf, m.Type)
|
||||||
|
}
|
||||||
|
buf.WriteByte('}')
|
||||||
|
|
||||||
|
case *Map:
|
||||||
|
buf.WriteString("map[")
|
||||||
|
writeType(buf, t.Key)
|
||||||
|
buf.WriteByte(']')
|
||||||
|
writeType(buf, t.Elt)
|
||||||
|
|
||||||
|
case *Chan:
|
||||||
|
var s string
|
||||||
|
switch t.Dir {
|
||||||
|
case ast.SEND:
|
||||||
|
s = "chan<- "
|
||||||
|
case ast.RECV:
|
||||||
|
s = "<-chan "
|
||||||
|
default:
|
||||||
|
s = "chan "
|
||||||
|
}
|
||||||
|
buf.WriteString(s)
|
||||||
|
writeType(buf, t.Elt)
|
||||||
|
|
||||||
|
case *NamedType:
|
||||||
|
s := "<NamedType w/o object>"
|
||||||
|
if obj := t.Obj; obj != nil {
|
||||||
|
if obj.Pkg != nil && obj.Pkg.Path != "" {
|
||||||
|
buf.WriteString(obj.Pkg.Path)
|
||||||
|
buf.WriteString(".")
|
||||||
|
}
|
||||||
|
s = t.Obj.GetName()
|
||||||
|
}
|
||||||
|
buf.WriteString(s)
|
||||||
|
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(buf, "<type %T>", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Array) String() string { return typeString(t) }
|
||||||
|
func (t *Basic) String() string { return typeString(t) }
|
||||||
|
func (t *Chan) String() string { return typeString(t) }
|
||||||
|
func (t *Interface) String() string { return typeString(t) }
|
||||||
|
func (t *Map) String() string { return typeString(t) }
|
||||||
|
func (t *NamedType) String() string { return typeString(t) }
|
||||||
|
func (t *Pointer) String() string { return typeString(t) }
|
||||||
|
func (t *Result) String() string { return typeString(t) }
|
||||||
|
func (t *Signature) String() string { return typeString(t) }
|
||||||
|
func (t *Slice) String() string { return typeString(t) }
|
||||||
|
func (t *Struct) String() string { return typeString(t) }
|
||||||
|
func (t *builtin) String() string { return typeString(t) }
|
111
go/types/exportdata.go
Normal file
111
go/types/exportdata.go
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// Copyright 2011 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 FindGcExportData.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
|
||||||
|
// See $GOROOT/include/ar.h.
|
||||||
|
hdr := make([]byte, 16+12+6+6+8+10+2)
|
||||||
|
_, err = io.ReadFull(r, hdr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// leave for debugging
|
||||||
|
if false {
|
||||||
|
fmt.Printf("header: %s", hdr)
|
||||||
|
}
|
||||||
|
s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
|
||||||
|
size, err = strconv.Atoi(s)
|
||||||
|
if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
|
||||||
|
err = errors.New("invalid archive header")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name = strings.TrimSpace(string(hdr[:16]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindGcExportData positions the reader r at the beginning of the
|
||||||
|
// export data section of an underlying GC-created object/archive
|
||||||
|
// file by reading from it. The reader must be positioned at the
|
||||||
|
// start of the file before calling this function.
|
||||||
|
//
|
||||||
|
func FindGcExportData(r *bufio.Reader) (err error) {
|
||||||
|
// Read first line to make sure this is an object file.
|
||||||
|
line, err := r.ReadSlice('\n')
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if string(line) == "!<arch>\n" {
|
||||||
|
// Archive file. Scan to __.PKGDEF, which should
|
||||||
|
// be second archive entry.
|
||||||
|
var name string
|
||||||
|
var size int
|
||||||
|
|
||||||
|
// First entry should be __.GOSYMDEF.
|
||||||
|
// Older archives used __.SYMDEF, so allow that too.
|
||||||
|
// Read and discard.
|
||||||
|
if name, size, err = readGopackHeader(r); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if name != "__.SYMDEF" && name != "__.GOSYMDEF" {
|
||||||
|
err = errors.New("go archive does not begin with __.SYMDEF or __.GOSYMDEF")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const block = 4096
|
||||||
|
tmp := make([]byte, block)
|
||||||
|
for size > 0 {
|
||||||
|
n := size
|
||||||
|
if n > block {
|
||||||
|
n = block
|
||||||
|
}
|
||||||
|
if _, err = io.ReadFull(r, tmp[:n]); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
size -= n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second entry should be __.PKGDEF.
|
||||||
|
if name, size, err = readGopackHeader(r); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if name != "__.PKGDEF" {
|
||||||
|
err = errors.New("go archive is missing __.PKGDEF")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read first line of __.PKGDEF data, so that line
|
||||||
|
// is once again the first line of the input.
|
||||||
|
if line, err = r.ReadSlice('\n'); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now at __.PKGDEF in archive or still at beginning of file.
|
||||||
|
// Either way, line should begin with "go object ".
|
||||||
|
if !strings.HasPrefix(string(line), "go object ") {
|
||||||
|
err = errors.New("not a go object file")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip over object header to export data.
|
||||||
|
// Begins after first line with $$.
|
||||||
|
for line[0] != '$' {
|
||||||
|
if line, err = r.ReadSlice('\n'); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
1758
go/types/expr.go
Normal file
1758
go/types/expr.go
Normal file
File diff suppressed because it is too large
Load Diff
945
go/types/gcimporter.go
Normal file
945
go/types/gcimporter.go
Normal file
@ -0,0 +1,945 @@
|
|||||||
|
// Copyright 2011 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 an Importer for gc-generated object files.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
|
"go/token"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"text/scanner"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pkgExts = [...]string{".a", ".5", ".6", ".8"}
|
||||||
|
|
||||||
|
// FindPkg returns the filename and unique package id for an import
|
||||||
|
// path based on package information provided by build.Import (using
|
||||||
|
// the build.Default build.Context).
|
||||||
|
// If no file was found, an empty filename is returned.
|
||||||
|
//
|
||||||
|
func FindPkg(path, srcDir string) (filename, id string) {
|
||||||
|
if len(path) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id = path
|
||||||
|
var noext string
|
||||||
|
switch {
|
||||||
|
default:
|
||||||
|
// "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
|
||||||
|
// Don't require the source files to be present.
|
||||||
|
bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
|
||||||
|
if bp.PkgObj == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
noext = strings.TrimSuffix(bp.PkgObj, ".a")
|
||||||
|
|
||||||
|
case build.IsLocalImport(path):
|
||||||
|
// "./x" -> "/this/directory/x.ext", "/this/directory/x"
|
||||||
|
noext = filepath.Join(srcDir, path)
|
||||||
|
id = noext
|
||||||
|
|
||||||
|
case filepath.IsAbs(path):
|
||||||
|
// for completeness only - go/build.Import
|
||||||
|
// does not support absolute imports
|
||||||
|
// "/x" -> "/x.ext", "/x"
|
||||||
|
noext = path
|
||||||
|
}
|
||||||
|
|
||||||
|
// try extensions
|
||||||
|
for _, ext := range pkgExts {
|
||||||
|
filename = noext + ext
|
||||||
|
if f, err := os.Stat(filename); err == nil && !f.IsDir() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = "" // not found
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GcImportData imports a package by reading the gc-generated export data,
|
||||||
|
// adds the corresponding package object to the imports map indexed by id,
|
||||||
|
// and returns the object.
|
||||||
|
//
|
||||||
|
// The imports map must contains all packages already imported. The data
|
||||||
|
// reader position must be the beginning of the export data section. The
|
||||||
|
// filename is only used in error messages.
|
||||||
|
//
|
||||||
|
// If imports[id] contains the completely imported package, that package
|
||||||
|
// can be used directly, and there is no need to call this function (but
|
||||||
|
// there is also no harm but for extra time used).
|
||||||
|
//
|
||||||
|
func GcImportData(imports map[string]*Package, filename, id string, data *bufio.Reader) (pkg *Package, err error) {
|
||||||
|
// support for gcParser error handling
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err = r.(importError) // will re-panic if r is not an importError
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var p gcParser
|
||||||
|
p.init(filename, id, data, imports)
|
||||||
|
pkg = p.parseExport()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GcImport imports a gc-generated package given its import path, adds the
|
||||||
|
// corresponding package object to the imports map, and returns the object.
|
||||||
|
// Local import paths are interpreted relative to the current working directory.
|
||||||
|
// The imports map must contains all packages already imported.
|
||||||
|
// GcImport satisfies the ast.Importer signature.
|
||||||
|
//
|
||||||
|
func GcImport(imports map[string]*Package, path string) (pkg *Package, err error) {
|
||||||
|
if path == "unsafe" {
|
||||||
|
return Unsafe, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
srcDir := "."
|
||||||
|
if build.IsLocalImport(path) {
|
||||||
|
srcDir, err = os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filename, id := FindPkg(path, srcDir)
|
||||||
|
if filename == "" {
|
||||||
|
err = errors.New("can't find import: " + id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// no need to re-import if the package was imported completely before
|
||||||
|
if pkg = imports[id]; pkg != nil && pkg.Complete {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// open file
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
// add file name to error
|
||||||
|
err = fmt.Errorf("reading export data: %s: %v", filename, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
buf := bufio.NewReader(f)
|
||||||
|
if err = FindGcExportData(buf); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg, err = GcImportData(imports, filename, id, buf)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// gcParser
|
||||||
|
|
||||||
|
// gcParser parses the exports inside a gc compiler-produced
|
||||||
|
// object/archive file and populates its scope with the results.
|
||||||
|
type gcParser struct {
|
||||||
|
scanner scanner.Scanner
|
||||||
|
tok rune // current token
|
||||||
|
lit string // literal string; only valid for Ident, Int, String tokens
|
||||||
|
id string // package id of imported package
|
||||||
|
imports map[string]*Package // package id -> package object
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]*Package) {
|
||||||
|
p.scanner.Init(src)
|
||||||
|
p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
|
||||||
|
p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
|
||||||
|
p.scanner.Whitespace = 1<<'\t' | 1<<' '
|
||||||
|
p.scanner.Filename = filename // for good error messages
|
||||||
|
p.next()
|
||||||
|
p.id = id
|
||||||
|
p.imports = imports
|
||||||
|
// leave for debugging
|
||||||
|
if false {
|
||||||
|
// check consistency of imports map
|
||||||
|
for _, pkg := range imports {
|
||||||
|
if pkg.Name == "" {
|
||||||
|
fmt.Printf("no package name for %s\n", pkg.Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *gcParser) next() {
|
||||||
|
p.tok = p.scanner.Scan()
|
||||||
|
switch p.tok {
|
||||||
|
case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·':
|
||||||
|
p.lit = p.scanner.TokenText()
|
||||||
|
default:
|
||||||
|
p.lit = ""
|
||||||
|
}
|
||||||
|
// leave for debugging
|
||||||
|
if false {
|
||||||
|
fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func declConst(pkg *Package, name string) *Const {
|
||||||
|
// the constant may have been imported before - if it exists
|
||||||
|
// already in the respective scope, return that constant
|
||||||
|
scope := pkg.Scope
|
||||||
|
if obj := scope.Lookup(name); obj != nil {
|
||||||
|
return obj.(*Const)
|
||||||
|
}
|
||||||
|
// otherwise create a new constant and insert it into the scope
|
||||||
|
obj := &Const{Pkg: pkg, Name: name}
|
||||||
|
scope.Insert(obj)
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func declTypeName(pkg *Package, name string) *TypeName {
|
||||||
|
scope := pkg.Scope
|
||||||
|
if obj := scope.Lookup(name); obj != nil {
|
||||||
|
return obj.(*TypeName)
|
||||||
|
}
|
||||||
|
obj := &TypeName{Pkg: pkg, Name: name}
|
||||||
|
// a named type may be referred to before the underlying type
|
||||||
|
// is known - set it up
|
||||||
|
obj.Type = &NamedType{Obj: obj}
|
||||||
|
scope.Insert(obj)
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func declVar(pkg *Package, name string) *Var {
|
||||||
|
scope := pkg.Scope
|
||||||
|
if obj := scope.Lookup(name); obj != nil {
|
||||||
|
return obj.(*Var)
|
||||||
|
}
|
||||||
|
obj := &Var{Pkg: pkg, Name: name}
|
||||||
|
scope.Insert(obj)
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func declFunc(pkg *Package, name string) *Func {
|
||||||
|
scope := pkg.Scope
|
||||||
|
if obj := scope.Lookup(name); obj != nil {
|
||||||
|
return obj.(*Func)
|
||||||
|
}
|
||||||
|
obj := &Func{Pkg: pkg, Name: name}
|
||||||
|
scope.Insert(obj)
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Error handling
|
||||||
|
|
||||||
|
// Internal errors are boxed as importErrors.
|
||||||
|
type importError struct {
|
||||||
|
pos scanner.Position
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e importError) Error() string {
|
||||||
|
return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *gcParser) error(err interface{}) {
|
||||||
|
if s, ok := err.(string); ok {
|
||||||
|
err = errors.New(s)
|
||||||
|
}
|
||||||
|
// panic with a runtime.Error if err is not an error
|
||||||
|
panic(importError{p.scanner.Pos(), err.(error)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *gcParser) errorf(format string, args ...interface{}) {
|
||||||
|
p.error(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *gcParser) expect(tok rune) string {
|
||||||
|
lit := p.lit
|
||||||
|
if p.tok != tok {
|
||||||
|
p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
|
||||||
|
}
|
||||||
|
p.next()
|
||||||
|
return lit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *gcParser) expectSpecial(tok string) {
|
||||||
|
sep := 'x' // not white space
|
||||||
|
i := 0
|
||||||
|
for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' {
|
||||||
|
sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
|
||||||
|
p.next()
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i < len(tok) {
|
||||||
|
p.errorf("expected %q, got %q", tok, tok[0:i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *gcParser) expectKeyword(keyword string) {
|
||||||
|
lit := p.expect(scanner.Ident)
|
||||||
|
if lit != keyword {
|
||||||
|
p.errorf("expected keyword %s, got %q", keyword, lit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Qualified and unqualified names
|
||||||
|
|
||||||
|
// PackageId = string_lit .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parsePackageId() string {
|
||||||
|
id, err := strconv.Unquote(p.expect(scanner.String))
|
||||||
|
if err != nil {
|
||||||
|
p.error(err)
|
||||||
|
}
|
||||||
|
// id == "" stands for the imported package id
|
||||||
|
// (only known at time of package installation)
|
||||||
|
if id == "" {
|
||||||
|
id = p.id
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackageName = ident .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parsePackageName() string {
|
||||||
|
return p.expect(scanner.Ident)
|
||||||
|
}
|
||||||
|
|
||||||
|
// dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
|
||||||
|
func (p *gcParser) parseDotIdent() string {
|
||||||
|
ident := ""
|
||||||
|
if p.tok != scanner.Int {
|
||||||
|
sep := 'x' // not white space
|
||||||
|
for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' {
|
||||||
|
ident += p.lit
|
||||||
|
sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ident == "" {
|
||||||
|
p.expect(scanner.Ident) // use expect() for error handling
|
||||||
|
}
|
||||||
|
return ident
|
||||||
|
}
|
||||||
|
|
||||||
|
// QualifiedName = "@" PackageId "." dotIdentifier .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseQualifiedName() (id, name string) {
|
||||||
|
p.expect('@')
|
||||||
|
id = p.parsePackageId()
|
||||||
|
p.expect('.')
|
||||||
|
name = p.parseDotIdent()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPkg returns the package for a given id. If the package is
|
||||||
|
// not found but we have a package name, create the package and
|
||||||
|
// add it to the p.imports map.
|
||||||
|
//
|
||||||
|
func (p *gcParser) getPkg(id, name string) *Package {
|
||||||
|
// package unsafe is not in the imports map - handle explicitly
|
||||||
|
if id == "unsafe" {
|
||||||
|
return Unsafe
|
||||||
|
}
|
||||||
|
pkg := p.imports[id]
|
||||||
|
if pkg == nil && name != "" {
|
||||||
|
pkg = &Package{Name: name, Path: id, Scope: new(Scope)}
|
||||||
|
p.imports[id] = pkg
|
||||||
|
}
|
||||||
|
return pkg
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseExportedName is like parseQualifiedName, but
|
||||||
|
// the package id is resolved to an imported *Package.
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseExportedName() (pkg *Package, name string) {
|
||||||
|
id, name := p.parseQualifiedName()
|
||||||
|
pkg = p.getPkg(id, "")
|
||||||
|
if pkg == nil {
|
||||||
|
p.errorf("%s package not found", id)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Types
|
||||||
|
|
||||||
|
// BasicType = identifier .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseBasicType() Type {
|
||||||
|
id := p.expect(scanner.Ident)
|
||||||
|
obj := Universe.Lookup(id)
|
||||||
|
if obj, ok := obj.(*TypeName); ok {
|
||||||
|
return obj.Type
|
||||||
|
}
|
||||||
|
p.errorf("not a basic type: %s", id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArrayType = "[" int_lit "]" Type .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseArrayType() Type {
|
||||||
|
// "[" already consumed and lookahead known not to be "]"
|
||||||
|
lit := p.expect(scanner.Int)
|
||||||
|
p.expect(']')
|
||||||
|
elt := p.parseType()
|
||||||
|
n, err := strconv.ParseInt(lit, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
p.error(err)
|
||||||
|
}
|
||||||
|
return &Array{Len: n, Elt: elt}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapType = "map" "[" Type "]" Type .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseMapType() Type {
|
||||||
|
p.expectKeyword("map")
|
||||||
|
p.expect('[')
|
||||||
|
key := p.parseType()
|
||||||
|
p.expect(']')
|
||||||
|
elt := p.parseType()
|
||||||
|
return &Map{Key: key, Elt: elt}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name = identifier | "?" | QualifiedName .
|
||||||
|
//
|
||||||
|
// If materializePkg is set, a package is returned for fully qualified names.
|
||||||
|
// That package may be a fake package (without name, scope, and not in the
|
||||||
|
// p.imports map), created for the sole purpose of providing a package path
|
||||||
|
// for QualifiedNames. Fake packages are created when the package id is not
|
||||||
|
// found in the p.imports map; we cannot create a real package in that case
|
||||||
|
// because we don't have a package name.
|
||||||
|
//
|
||||||
|
// TODO(gri): consider changing QualifiedIdents to (path, name) pairs to
|
||||||
|
// simplify this code.
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) {
|
||||||
|
switch p.tok {
|
||||||
|
case scanner.Ident:
|
||||||
|
name = p.lit
|
||||||
|
p.next()
|
||||||
|
case '?':
|
||||||
|
// anonymous
|
||||||
|
p.next()
|
||||||
|
case '@':
|
||||||
|
// exported name prefixed with package path
|
||||||
|
var id string
|
||||||
|
id, name = p.parseQualifiedName()
|
||||||
|
if materializePkg {
|
||||||
|
// we don't have a package name - if the package
|
||||||
|
// doesn't exist yet, create a fake package instead
|
||||||
|
pkg = p.getPkg(id, "")
|
||||||
|
if pkg == nil {
|
||||||
|
pkg = &Package{Path: id}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
p.error("name expected")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field = Name Type [ string_lit ] .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseField() *Field {
|
||||||
|
var f Field
|
||||||
|
f.Pkg, f.Name = p.parseName(true)
|
||||||
|
f.Type = p.parseType()
|
||||||
|
if p.tok == scanner.String {
|
||||||
|
f.Tag = p.expect(scanner.String)
|
||||||
|
}
|
||||||
|
if f.Name == "" {
|
||||||
|
// anonymous field - typ must be T or *T and T must be a type name
|
||||||
|
if typ, ok := deref(f.Type).(*NamedType); ok && typ.Obj != nil {
|
||||||
|
f.Name = typ.Obj.GetName()
|
||||||
|
f.IsAnonymous = true
|
||||||
|
} else {
|
||||||
|
p.errorf("anonymous field expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &f
|
||||||
|
}
|
||||||
|
|
||||||
|
// StructType = "struct" "{" [ FieldList ] "}" .
|
||||||
|
// FieldList = Field { ";" Field } .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseStructType() Type {
|
||||||
|
var fields []*Field
|
||||||
|
|
||||||
|
p.expectKeyword("struct")
|
||||||
|
p.expect('{')
|
||||||
|
for p.tok != '}' {
|
||||||
|
if len(fields) > 0 {
|
||||||
|
p.expect(';')
|
||||||
|
}
|
||||||
|
fields = append(fields, p.parseField())
|
||||||
|
}
|
||||||
|
p.expect('}')
|
||||||
|
|
||||||
|
return &Struct{Fields: fields}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseParameter() (par *Var, isVariadic bool) {
|
||||||
|
_, name := p.parseName(false)
|
||||||
|
if name == "" {
|
||||||
|
name = "_" // cannot access unnamed identifiers
|
||||||
|
}
|
||||||
|
if p.tok == '.' {
|
||||||
|
p.expectSpecial("...")
|
||||||
|
isVariadic = true
|
||||||
|
}
|
||||||
|
typ := p.parseType()
|
||||||
|
// ignore argument tag (e.g. "noescape")
|
||||||
|
if p.tok == scanner.String {
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
par = &Var{Name: name, Type: typ} // Pkg == nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameters = "(" [ ParameterList ] ")" .
|
||||||
|
// ParameterList = { Parameter "," } Parameter .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseParameters() (list []*Var, isVariadic bool) {
|
||||||
|
p.expect('(')
|
||||||
|
for p.tok != ')' {
|
||||||
|
if len(list) > 0 {
|
||||||
|
p.expect(',')
|
||||||
|
}
|
||||||
|
par, variadic := p.parseParameter()
|
||||||
|
list = append(list, par)
|
||||||
|
if variadic {
|
||||||
|
if isVariadic {
|
||||||
|
p.error("... not on final argument")
|
||||||
|
}
|
||||||
|
isVariadic = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.expect(')')
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature = Parameters [ Result ] .
|
||||||
|
// Result = Type | Parameters .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseSignature() *Signature {
|
||||||
|
params, isVariadic := p.parseParameters()
|
||||||
|
|
||||||
|
// optional result type
|
||||||
|
var results []*Var
|
||||||
|
if p.tok == '(' {
|
||||||
|
var variadic bool
|
||||||
|
results, variadic = p.parseParameters()
|
||||||
|
if variadic {
|
||||||
|
p.error("... not permitted on result type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Signature{Params: params, Results: results, IsVariadic: isVariadic}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterfaceType = "interface" "{" [ MethodList ] "}" .
|
||||||
|
// MethodList = Method { ";" Method } .
|
||||||
|
// Method = Name Signature .
|
||||||
|
//
|
||||||
|
// The methods of embedded interfaces are always "inlined"
|
||||||
|
// by the compiler and thus embedded interfaces are never
|
||||||
|
// visible in the export data.
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseInterfaceType() Type {
|
||||||
|
var methods []*Method
|
||||||
|
|
||||||
|
p.expectKeyword("interface")
|
||||||
|
p.expect('{')
|
||||||
|
for p.tok != '}' {
|
||||||
|
if len(methods) > 0 {
|
||||||
|
p.expect(';')
|
||||||
|
}
|
||||||
|
pkg, name := p.parseName(true)
|
||||||
|
typ := p.parseSignature()
|
||||||
|
methods = append(methods, &Method{QualifiedName{pkg, name}, typ})
|
||||||
|
}
|
||||||
|
p.expect('}')
|
||||||
|
|
||||||
|
return &Interface{Methods: methods}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseChanType() Type {
|
||||||
|
dir := ast.SEND | ast.RECV
|
||||||
|
if p.tok == scanner.Ident {
|
||||||
|
p.expectKeyword("chan")
|
||||||
|
if p.tok == '<' {
|
||||||
|
p.expectSpecial("<-")
|
||||||
|
dir = ast.SEND
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.expectSpecial("<-")
|
||||||
|
p.expectKeyword("chan")
|
||||||
|
dir = ast.RECV
|
||||||
|
}
|
||||||
|
elt := p.parseType()
|
||||||
|
return &Chan{Dir: dir, Elt: elt}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type =
|
||||||
|
// BasicType | TypeName | ArrayType | SliceType | StructType |
|
||||||
|
// PointerType | FuncType | InterfaceType | MapType | ChanType |
|
||||||
|
// "(" Type ")" .
|
||||||
|
//
|
||||||
|
// BasicType = ident .
|
||||||
|
// TypeName = ExportedName .
|
||||||
|
// SliceType = "[" "]" Type .
|
||||||
|
// PointerType = "*" Type .
|
||||||
|
// FuncType = "func" Signature .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseType() Type {
|
||||||
|
switch p.tok {
|
||||||
|
case scanner.Ident:
|
||||||
|
switch p.lit {
|
||||||
|
default:
|
||||||
|
return p.parseBasicType()
|
||||||
|
case "struct":
|
||||||
|
return p.parseStructType()
|
||||||
|
case "func":
|
||||||
|
// FuncType
|
||||||
|
p.next()
|
||||||
|
return p.parseSignature()
|
||||||
|
case "interface":
|
||||||
|
return p.parseInterfaceType()
|
||||||
|
case "map":
|
||||||
|
return p.parseMapType()
|
||||||
|
case "chan":
|
||||||
|
return p.parseChanType()
|
||||||
|
}
|
||||||
|
case '@':
|
||||||
|
// TypeName
|
||||||
|
pkg, name := p.parseExportedName()
|
||||||
|
return declTypeName(pkg, name).Type
|
||||||
|
case '[':
|
||||||
|
p.next() // look ahead
|
||||||
|
if p.tok == ']' {
|
||||||
|
// SliceType
|
||||||
|
p.next()
|
||||||
|
return &Slice{Elt: p.parseType()}
|
||||||
|
}
|
||||||
|
return p.parseArrayType()
|
||||||
|
case '*':
|
||||||
|
// PointerType
|
||||||
|
p.next()
|
||||||
|
return &Pointer{Base: p.parseType()}
|
||||||
|
case '<':
|
||||||
|
return p.parseChanType()
|
||||||
|
case '(':
|
||||||
|
// "(" Type ")"
|
||||||
|
p.next()
|
||||||
|
typ := p.parseType()
|
||||||
|
p.expect(')')
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Declarations
|
||||||
|
|
||||||
|
// ImportDecl = "import" PackageName PackageId .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseImportDecl() {
|
||||||
|
p.expectKeyword("import")
|
||||||
|
name := p.parsePackageName()
|
||||||
|
p.getPkg(p.parsePackageId(), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// int_lit = [ "+" | "-" ] { "0" ... "9" } .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseInt() string {
|
||||||
|
s := ""
|
||||||
|
switch p.tok {
|
||||||
|
case '-':
|
||||||
|
s = "-"
|
||||||
|
p.next()
|
||||||
|
case '+':
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
return s + p.expect(scanner.Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
// number = int_lit [ "p" int_lit ] .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseNumber() (x operand) {
|
||||||
|
x.mode = constant
|
||||||
|
|
||||||
|
// mantissa
|
||||||
|
mant := exact.MakeFromLiteral(p.parseInt(), token.INT)
|
||||||
|
assert(mant != nil)
|
||||||
|
|
||||||
|
if p.lit == "p" {
|
||||||
|
// exponent (base 2)
|
||||||
|
p.next()
|
||||||
|
exp, err := strconv.ParseInt(p.parseInt(), 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
p.error(err)
|
||||||
|
}
|
||||||
|
if exp < 0 {
|
||||||
|
denom := exact.MakeInt64(1)
|
||||||
|
denom = exact.Shift(denom, token.SHL, uint(-exp))
|
||||||
|
x.typ = Typ[UntypedFloat]
|
||||||
|
x.val = exact.BinaryOp(mant, token.QUO, denom)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if exp > 0 {
|
||||||
|
mant = exact.Shift(mant, token.SHL, uint(exp))
|
||||||
|
}
|
||||||
|
x.typ = Typ[UntypedFloat]
|
||||||
|
x.val = mant
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.typ = Typ[UntypedInt]
|
||||||
|
x.val = mant
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConstDecl = "const" ExportedName [ Type ] "=" Literal .
|
||||||
|
// Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit .
|
||||||
|
// bool_lit = "true" | "false" .
|
||||||
|
// complex_lit = "(" float_lit "+" float_lit "i" ")" .
|
||||||
|
// rune_lit = "(" int_lit "+" int_lit ")" .
|
||||||
|
// string_lit = `"` { unicode_char } `"` .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseConstDecl() {
|
||||||
|
p.expectKeyword("const")
|
||||||
|
pkg, name := p.parseExportedName()
|
||||||
|
obj := declConst(pkg, name)
|
||||||
|
var x operand
|
||||||
|
if p.tok != '=' {
|
||||||
|
obj.Type = p.parseType()
|
||||||
|
}
|
||||||
|
p.expect('=')
|
||||||
|
switch p.tok {
|
||||||
|
case scanner.Ident:
|
||||||
|
// bool_lit
|
||||||
|
if p.lit != "true" && p.lit != "false" {
|
||||||
|
p.error("expected true or false")
|
||||||
|
}
|
||||||
|
x.typ = Typ[UntypedBool]
|
||||||
|
x.val = exact.MakeBool(p.lit == "true")
|
||||||
|
p.next()
|
||||||
|
|
||||||
|
case '-', scanner.Int:
|
||||||
|
// int_lit
|
||||||
|
x = p.parseNumber()
|
||||||
|
|
||||||
|
case '(':
|
||||||
|
// complex_lit or rune_lit
|
||||||
|
p.next()
|
||||||
|
if p.tok == scanner.Char {
|
||||||
|
p.next()
|
||||||
|
p.expect('+')
|
||||||
|
x = p.parseNumber()
|
||||||
|
x.typ = Typ[UntypedRune]
|
||||||
|
p.expect(')')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
re := p.parseNumber()
|
||||||
|
p.expect('+')
|
||||||
|
im := p.parseNumber()
|
||||||
|
p.expectKeyword("i")
|
||||||
|
p.expect(')')
|
||||||
|
x.typ = Typ[UntypedComplex]
|
||||||
|
// TODO(gri) fix this
|
||||||
|
_, _ = re, im
|
||||||
|
x.val = exact.MakeInt64(0)
|
||||||
|
|
||||||
|
case scanner.Char:
|
||||||
|
// rune_lit
|
||||||
|
x.setConst(token.CHAR, p.lit)
|
||||||
|
p.next()
|
||||||
|
|
||||||
|
case scanner.String:
|
||||||
|
// string_lit
|
||||||
|
x.setConst(token.STRING, p.lit)
|
||||||
|
p.next()
|
||||||
|
|
||||||
|
default:
|
||||||
|
p.errorf("expected literal got %s", scanner.TokenString(p.tok))
|
||||||
|
}
|
||||||
|
if obj.Type == nil {
|
||||||
|
obj.Type = x.typ
|
||||||
|
}
|
||||||
|
assert(x.val != nil)
|
||||||
|
obj.Val = x.val
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeDecl = "type" ExportedName Type .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseTypeDecl() {
|
||||||
|
p.expectKeyword("type")
|
||||||
|
pkg, name := p.parseExportedName()
|
||||||
|
obj := declTypeName(pkg, name)
|
||||||
|
|
||||||
|
// The type object may have been imported before and thus already
|
||||||
|
// have a type associated with it. We still need to parse the type
|
||||||
|
// structure, but throw it away if the object already has a type.
|
||||||
|
// This ensures that all imports refer to the same type object for
|
||||||
|
// a given type declaration.
|
||||||
|
typ := p.parseType()
|
||||||
|
|
||||||
|
if name := obj.Type.(*NamedType); name.Underlying == nil {
|
||||||
|
name.Underlying = typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VarDecl = "var" ExportedName Type .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseVarDecl() {
|
||||||
|
p.expectKeyword("var")
|
||||||
|
pkg, name := p.parseExportedName()
|
||||||
|
obj := declVar(pkg, name)
|
||||||
|
obj.Type = p.parseType()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Func = Signature [ Body ] .
|
||||||
|
// Body = "{" ... "}" .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseFunc() *Signature {
|
||||||
|
sig := p.parseSignature()
|
||||||
|
if p.tok == '{' {
|
||||||
|
p.next()
|
||||||
|
for i := 1; i > 0; p.next() {
|
||||||
|
switch p.tok {
|
||||||
|
case '{':
|
||||||
|
i++
|
||||||
|
case '}':
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sig
|
||||||
|
}
|
||||||
|
|
||||||
|
// MethodDecl = "func" Receiver Name Func .
|
||||||
|
// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseMethodDecl() {
|
||||||
|
// "func" already consumed
|
||||||
|
p.expect('(')
|
||||||
|
recv, _ := p.parseParameter() // receiver
|
||||||
|
p.expect(')')
|
||||||
|
|
||||||
|
// determine receiver base type object
|
||||||
|
typ := recv.Type
|
||||||
|
if ptr, ok := typ.(*Pointer); ok {
|
||||||
|
typ = ptr.Base
|
||||||
|
}
|
||||||
|
base := typ.(*NamedType)
|
||||||
|
|
||||||
|
// parse method name, signature, and possibly inlined body
|
||||||
|
pkg, name := p.parseName(true) // unexported method names in imports are qualified with their package.
|
||||||
|
sig := p.parseFunc()
|
||||||
|
sig.Recv = recv
|
||||||
|
|
||||||
|
// add method to type unless type was imported before
|
||||||
|
// and method exists already
|
||||||
|
// TODO(gri) investigate if this can be avoided
|
||||||
|
for _, m := range base.Methods {
|
||||||
|
if m.Name == name {
|
||||||
|
return // method was added before
|
||||||
|
}
|
||||||
|
}
|
||||||
|
base.Methods = append(base.Methods, &Method{QualifiedName{pkg, name}, sig})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncDecl = "func" ExportedName Func .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseFuncDecl() {
|
||||||
|
// "func" already consumed
|
||||||
|
pkg, name := p.parseExportedName()
|
||||||
|
typ := p.parseFunc()
|
||||||
|
declFunc(pkg, name).Type = typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseDecl() {
|
||||||
|
switch p.lit {
|
||||||
|
case "import":
|
||||||
|
p.parseImportDecl()
|
||||||
|
case "const":
|
||||||
|
p.parseConstDecl()
|
||||||
|
case "type":
|
||||||
|
p.parseTypeDecl()
|
||||||
|
case "var":
|
||||||
|
p.parseVarDecl()
|
||||||
|
case "func":
|
||||||
|
p.next() // look ahead
|
||||||
|
if p.tok == '(' {
|
||||||
|
p.parseMethodDecl()
|
||||||
|
} else {
|
||||||
|
p.parseFuncDecl()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.expect('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Export
|
||||||
|
|
||||||
|
// Export = "PackageClause { Decl } "$$" .
|
||||||
|
// PackageClause = "package" PackageName [ "safe" ] "\n" .
|
||||||
|
//
|
||||||
|
func (p *gcParser) parseExport() *Package {
|
||||||
|
p.expectKeyword("package")
|
||||||
|
name := p.parsePackageName()
|
||||||
|
if p.tok != '\n' {
|
||||||
|
// A package is safe if it was compiled with the -u flag,
|
||||||
|
// which disables the unsafe package.
|
||||||
|
// TODO(gri) remember "safe" package
|
||||||
|
p.expectKeyword("safe")
|
||||||
|
}
|
||||||
|
p.expect('\n')
|
||||||
|
|
||||||
|
pkg := p.getPkg(p.id, name)
|
||||||
|
|
||||||
|
for p.tok != '$' && p.tok != scanner.EOF {
|
||||||
|
p.parseDecl()
|
||||||
|
}
|
||||||
|
|
||||||
|
if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' {
|
||||||
|
// don't call next()/expect() since reading past the
|
||||||
|
// export data may cause scanner errors (e.g. NUL chars)
|
||||||
|
p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n := p.scanner.ErrorCount; n != 0 {
|
||||||
|
p.errorf("expected no scanner errors, got %d", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// package was imported completely and without errors
|
||||||
|
pkg.Complete = true
|
||||||
|
|
||||||
|
return pkg
|
||||||
|
}
|
180
go/types/gcimporter_test.go
Normal file
180
go/types/gcimporter_test.go
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
// Copyright 2011 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 (
|
||||||
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var gcPath string // Go compiler path
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// determine compiler
|
||||||
|
var gc string
|
||||||
|
switch runtime.GOARCH {
|
||||||
|
case "386":
|
||||||
|
gc = "8g"
|
||||||
|
case "amd64":
|
||||||
|
gc = "6g"
|
||||||
|
case "arm":
|
||||||
|
gc = "5g"
|
||||||
|
default:
|
||||||
|
gcPath = "unknown-GOARCH-compiler"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gcPath = filepath.Join(build.ToolDir, gc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func compile(t *testing.T, dirname, filename string) string {
|
||||||
|
cmd := exec.Command(gcPath, filename)
|
||||||
|
cmd.Dir = dirname
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("%s", out)
|
||||||
|
t.Fatalf("%s %s failed: %s", gcPath, filename, err)
|
||||||
|
}
|
||||||
|
archCh, _ := build.ArchChar(runtime.GOARCH)
|
||||||
|
// filename should end with ".go"
|
||||||
|
return filepath.Join(dirname, filename[:len(filename)-2]+archCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the same global imports map for all tests. The effect is
|
||||||
|
// as if all tested packages were imported into a single package.
|
||||||
|
var imports = make(map[string]*Package)
|
||||||
|
|
||||||
|
func testPath(t *testing.T, path string) bool {
|
||||||
|
t0 := time.Now()
|
||||||
|
_, err := GcImport(imports, path)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("testPath(%s): %s", path, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
t.Logf("testPath(%s): %v", path, time.Since(t0))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxTime = 30 * time.Second
|
||||||
|
|
||||||
|
func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
|
||||||
|
dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
|
||||||
|
list, err := ioutil.ReadDir(dirname)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("testDir(%s): %s", dirname, err)
|
||||||
|
}
|
||||||
|
for _, f := range list {
|
||||||
|
if time.Now().After(endTime) {
|
||||||
|
t.Log("testing time used up")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case !f.IsDir():
|
||||||
|
// try extensions
|
||||||
|
for _, ext := range pkgExts {
|
||||||
|
if strings.HasSuffix(f.Name(), ext) {
|
||||||
|
name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension
|
||||||
|
if testPath(t, filepath.Join(dir, name)) {
|
||||||
|
nimports++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case f.IsDir():
|
||||||
|
nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGcImport(t *testing.T) {
|
||||||
|
// On cross-compile builds, the path will not exist.
|
||||||
|
// Need to use GOHOSTOS, which is not available.
|
||||||
|
if _, err := os.Stat(gcPath); err != nil {
|
||||||
|
t.Skipf("skipping test: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if outFn := compile(t, "testdata", "exports.go"); outFn != "" {
|
||||||
|
defer os.Remove(outFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
nimports := 0
|
||||||
|
if testPath(t, "./testdata/exports") {
|
||||||
|
nimports++
|
||||||
|
}
|
||||||
|
nimports += testDir(t, "", time.Now().Add(maxTime)) // installed packages
|
||||||
|
t.Logf("tested %d imports", nimports)
|
||||||
|
}
|
||||||
|
|
||||||
|
var importedObjectTests = []struct {
|
||||||
|
name string
|
||||||
|
kind ast.ObjKind
|
||||||
|
typ string
|
||||||
|
}{
|
||||||
|
{"unsafe.Pointer", ast.Typ, "Pointer"},
|
||||||
|
{"math.Pi", ast.Con, "untyped float"},
|
||||||
|
{"io.Reader", ast.Typ, "interface{Read(p []byte) (n int, err error)}"},
|
||||||
|
{"io.ReadWriter", ast.Typ, "interface{Read(p []byte) (n int, err error); Write(p []byte) (n int, err error)}"},
|
||||||
|
{"math.Sin", ast.Fun, "func(x·2 float64) (_ float64)"},
|
||||||
|
// TODO(gri) add more tests
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGcImportedTypes(t *testing.T) {
|
||||||
|
// This package does not yet know how to read gccgo export data.
|
||||||
|
if runtime.Compiler == "gccgo" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, test := range importedObjectTests {
|
||||||
|
s := strings.Split(test.name, ".")
|
||||||
|
if len(s) != 2 {
|
||||||
|
t.Fatal("inconsistent test data")
|
||||||
|
}
|
||||||
|
importPath := s[0]
|
||||||
|
objName := s[1]
|
||||||
|
|
||||||
|
pkg, err := GcImport(imports, importPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := pkg.Scope.Lookup(objName)
|
||||||
|
|
||||||
|
// TODO(gri) should define an accessor on Object
|
||||||
|
var kind ast.ObjKind
|
||||||
|
var typ Type
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *Const:
|
||||||
|
kind = ast.Con
|
||||||
|
typ = obj.Type
|
||||||
|
case *TypeName:
|
||||||
|
kind = ast.Typ
|
||||||
|
typ = obj.Type
|
||||||
|
case *Var:
|
||||||
|
kind = ast.Var
|
||||||
|
typ = obj.Type
|
||||||
|
case *Func:
|
||||||
|
kind = ast.Fun
|
||||||
|
typ = obj.Type
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
if kind != test.kind {
|
||||||
|
t.Errorf("%s: got kind = %q; want %q", test.name, kind, test.kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
str := typeString(underlying(typ))
|
||||||
|
if str != test.typ {
|
||||||
|
t.Errorf("%s: got type = %q; want %q", test.name, typ, test.typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
193
go/types/objects.go
Normal file
193
go/types/objects.go
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
// Copyright 2013 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 (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An Object describes a named language entity such as a package,
|
||||||
|
// constant, type, variable, function (incl. methods), or label.
|
||||||
|
// All objects implement the Object interface.
|
||||||
|
//
|
||||||
|
type Object interface {
|
||||||
|
GetPkg() *Package
|
||||||
|
GetName() string
|
||||||
|
GetType() Type
|
||||||
|
GetPos() token.Pos
|
||||||
|
|
||||||
|
anObject()
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Package represents the contents (objects) of a Go package.
|
||||||
|
type Package struct {
|
||||||
|
Name string
|
||||||
|
Path string // import path, "" for current (non-imported) package
|
||||||
|
Scope *Scope // package-level scope
|
||||||
|
Imports map[string]*Package // map of import paths to imported packages
|
||||||
|
Complete bool // if set, this package was imported completely
|
||||||
|
|
||||||
|
spec *ast.ImportSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Const represents a declared constant.
|
||||||
|
type Const struct {
|
||||||
|
Pkg *Package
|
||||||
|
Name string
|
||||||
|
Type Type
|
||||||
|
Val exact.Value
|
||||||
|
|
||||||
|
visited bool // for initialization cycle detection
|
||||||
|
spec *ast.ValueSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
// A TypeName represents a declared type.
|
||||||
|
type TypeName struct {
|
||||||
|
Pkg *Package
|
||||||
|
Name string
|
||||||
|
Type Type // *NamedType or *Basic
|
||||||
|
|
||||||
|
spec *ast.TypeSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Variable represents a declared variable (including function parameters and results).
|
||||||
|
type Var struct {
|
||||||
|
Pkg *Package // nil for parameters
|
||||||
|
Name string
|
||||||
|
Type Type
|
||||||
|
|
||||||
|
visited bool // for initialization cycle detection
|
||||||
|
decl interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Func represents a declared function.
|
||||||
|
type Func struct {
|
||||||
|
Pkg *Package
|
||||||
|
Name string
|
||||||
|
Type Type // *Signature or *Builtin
|
||||||
|
|
||||||
|
decl *ast.FuncDecl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *Package) GetPkg() *Package { return obj }
|
||||||
|
func (obj *Const) GetPkg() *Package { return obj.Pkg }
|
||||||
|
func (obj *TypeName) GetPkg() *Package { return obj.Pkg }
|
||||||
|
func (obj *Var) GetPkg() *Package { return obj.Pkg }
|
||||||
|
func (obj *Func) GetPkg() *Package { return obj.Pkg }
|
||||||
|
|
||||||
|
func (obj *Package) GetName() string { return obj.Name }
|
||||||
|
func (obj *Const) GetName() string { return obj.Name }
|
||||||
|
func (obj *TypeName) GetName() string { return obj.Name }
|
||||||
|
func (obj *Var) GetName() string { return obj.Name }
|
||||||
|
func (obj *Func) GetName() string { return obj.Name }
|
||||||
|
|
||||||
|
func (obj *Package) GetType() Type { return Typ[Invalid] }
|
||||||
|
func (obj *Const) GetType() Type { return obj.Type }
|
||||||
|
func (obj *TypeName) GetType() Type { return obj.Type }
|
||||||
|
func (obj *Var) GetType() Type { return obj.Type }
|
||||||
|
func (obj *Func) GetType() Type { return obj.Type }
|
||||||
|
|
||||||
|
func (obj *Package) GetPos() token.Pos {
|
||||||
|
if obj.spec == nil {
|
||||||
|
return token.NoPos
|
||||||
|
}
|
||||||
|
return obj.spec.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *Const) GetPos() token.Pos {
|
||||||
|
if obj.spec == nil {
|
||||||
|
return token.NoPos
|
||||||
|
}
|
||||||
|
for _, n := range obj.spec.Names {
|
||||||
|
if n.Name == obj.Name {
|
||||||
|
return n.Pos()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return token.NoPos
|
||||||
|
}
|
||||||
|
func (obj *TypeName) GetPos() token.Pos {
|
||||||
|
if obj.spec == nil {
|
||||||
|
return token.NoPos
|
||||||
|
}
|
||||||
|
return obj.spec.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *Var) GetPos() token.Pos {
|
||||||
|
switch d := obj.decl.(type) {
|
||||||
|
case *ast.Field:
|
||||||
|
for _, n := range d.Names {
|
||||||
|
if n.Name == obj.Name {
|
||||||
|
return n.Pos()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ast.ValueSpec:
|
||||||
|
for _, n := range d.Names {
|
||||||
|
if n.Name == obj.Name {
|
||||||
|
return n.Pos()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
for _, x := range d.Lhs {
|
||||||
|
if ident, isIdent := x.(*ast.Ident); isIdent && ident.Name == obj.Name {
|
||||||
|
return ident.Pos()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return token.NoPos
|
||||||
|
}
|
||||||
|
func (obj *Func) GetPos() token.Pos {
|
||||||
|
if obj.decl != nil && obj.decl.Name != nil {
|
||||||
|
return obj.decl.Name.Pos()
|
||||||
|
}
|
||||||
|
return token.NoPos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Package) anObject() {}
|
||||||
|
func (*Const) anObject() {}
|
||||||
|
func (*TypeName) anObject() {}
|
||||||
|
func (*Var) anObject() {}
|
||||||
|
func (*Func) anObject() {}
|
||||||
|
|
||||||
|
// newObj returns a new Object for a given *ast.Object.
|
||||||
|
// It does not canonicalize them (it always returns a new one).
|
||||||
|
// For canonicalization, see check.lookup.
|
||||||
|
//
|
||||||
|
// TODO(gri) Once we do identifier resolution completely in
|
||||||
|
// the typechecker, this functionality can go.
|
||||||
|
//
|
||||||
|
func newObj(pkg *Package, astObj *ast.Object) Object {
|
||||||
|
assert(pkg != nil)
|
||||||
|
name := astObj.Name
|
||||||
|
typ, _ := astObj.Type.(Type)
|
||||||
|
switch astObj.Kind {
|
||||||
|
case ast.Bad:
|
||||||
|
// ignore
|
||||||
|
case ast.Pkg:
|
||||||
|
unreachable()
|
||||||
|
case ast.Con:
|
||||||
|
iota := astObj.Data.(int)
|
||||||
|
return &Const{Pkg: pkg, Name: name, Type: typ, Val: exact.MakeInt64(int64(iota)), spec: astObj.Decl.(*ast.ValueSpec)}
|
||||||
|
case ast.Typ:
|
||||||
|
return &TypeName{Pkg: pkg, Name: name, Type: typ, spec: astObj.Decl.(*ast.TypeSpec)}
|
||||||
|
case ast.Var:
|
||||||
|
switch astObj.Decl.(type) {
|
||||||
|
case *ast.Field: // function parameters
|
||||||
|
case *ast.ValueSpec: // proper variable declarations
|
||||||
|
case *ast.AssignStmt: // short variable declarations
|
||||||
|
default:
|
||||||
|
unreachable() // everything else is not ok
|
||||||
|
}
|
||||||
|
return &Var{Pkg: pkg, Name: name, Type: typ, decl: astObj.Decl}
|
||||||
|
case ast.Fun:
|
||||||
|
return &Func{Pkg: pkg, Name: name, Type: typ, decl: astObj.Decl.(*ast.FuncDecl)}
|
||||||
|
case ast.Lbl:
|
||||||
|
unreachable() // for now
|
||||||
|
}
|
||||||
|
unreachable()
|
||||||
|
return nil
|
||||||
|
}
|
406
go/types/operand.go
Normal file
406
go/types/operand.go
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
// Copyright 2012 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 defines operands and associated operations.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An operandMode specifies the (addressing) mode of an operand.
|
||||||
|
type operandMode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
invalid operandMode = iota // operand is invalid (due to an earlier error) - ignore
|
||||||
|
novalue // operand represents no value (result of a function call w/o result)
|
||||||
|
typexpr // operand is a type
|
||||||
|
constant // operand is a constant; the operand's typ is a Basic type
|
||||||
|
variable // operand is an addressable variable
|
||||||
|
value // operand is a computed value
|
||||||
|
valueok // like mode == value, but operand may be used in a comma,ok expression
|
||||||
|
)
|
||||||
|
|
||||||
|
var operandModeString = [...]string{
|
||||||
|
invalid: "invalid",
|
||||||
|
novalue: "no value",
|
||||||
|
typexpr: "type",
|
||||||
|
constant: "constant",
|
||||||
|
variable: "variable",
|
||||||
|
value: "value",
|
||||||
|
valueok: "value,ok",
|
||||||
|
}
|
||||||
|
|
||||||
|
// An operand represents an intermediate value during type checking.
|
||||||
|
// Operands have an (addressing) mode, the expression evaluating to
|
||||||
|
// the operand, the operand's type, and for constants a constant value.
|
||||||
|
//
|
||||||
|
type operand struct {
|
||||||
|
mode operandMode
|
||||||
|
expr ast.Expr
|
||||||
|
typ Type
|
||||||
|
val exact.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// pos returns the position of the expression corresponding to x.
|
||||||
|
// If x is invalid the position is token.NoPos.
|
||||||
|
//
|
||||||
|
func (x *operand) pos() token.Pos {
|
||||||
|
// x.expr may not be set if x is invalid
|
||||||
|
if x.expr == nil {
|
||||||
|
return token.NoPos
|
||||||
|
}
|
||||||
|
return x.expr.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *operand) String() string {
|
||||||
|
if x.mode == invalid {
|
||||||
|
return "invalid operand"
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if x.expr != nil {
|
||||||
|
buf.WriteString(exprString(x.expr))
|
||||||
|
buf.WriteString(" (")
|
||||||
|
}
|
||||||
|
buf.WriteString(operandModeString[x.mode])
|
||||||
|
if x.mode == constant {
|
||||||
|
format := " %v"
|
||||||
|
if isString(x.typ) {
|
||||||
|
format = " %q"
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&buf, format, x.val)
|
||||||
|
}
|
||||||
|
if x.mode != novalue && (x.mode != constant || !isUntyped(x.typ)) {
|
||||||
|
fmt.Fprintf(&buf, " of type %s", typeString(x.typ))
|
||||||
|
}
|
||||||
|
if x.expr != nil {
|
||||||
|
buf.WriteByte(')')
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// setConst sets x to the untyped constant for literal lit.
|
||||||
|
func (x *operand) setConst(tok token.Token, lit string) {
|
||||||
|
val := exact.MakeFromLiteral(lit, tok)
|
||||||
|
if val == nil {
|
||||||
|
// TODO(gri) Should we make it an unknown constant instead?
|
||||||
|
x.mode = invalid
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var kind BasicKind
|
||||||
|
switch tok {
|
||||||
|
case token.INT:
|
||||||
|
kind = UntypedInt
|
||||||
|
case token.FLOAT:
|
||||||
|
kind = UntypedFloat
|
||||||
|
case token.IMAG:
|
||||||
|
kind = UntypedComplex
|
||||||
|
case token.CHAR:
|
||||||
|
kind = UntypedRune
|
||||||
|
case token.STRING:
|
||||||
|
kind = UntypedString
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = constant
|
||||||
|
x.typ = Typ[kind]
|
||||||
|
x.val = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// isNil reports whether x is the predeclared nil constant.
|
||||||
|
func (x *operand) isNil() bool {
|
||||||
|
return x.mode == constant && x.val.Kind() == exact.Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) The functions operand.isAssignable, checker.convertUntyped,
|
||||||
|
// checker.isRepresentable, and checker.assignOperand are
|
||||||
|
// overlapping in functionality. Need to simplify and clean up.
|
||||||
|
|
||||||
|
// isAssignable reports whether x is assignable to a variable of type T.
|
||||||
|
func (x *operand) isAssignable(ctxt *Context, T Type) bool {
|
||||||
|
if x.mode == invalid || T == Typ[Invalid] {
|
||||||
|
return true // avoid spurious errors
|
||||||
|
}
|
||||||
|
|
||||||
|
V := x.typ
|
||||||
|
|
||||||
|
// x's type is identical to T
|
||||||
|
if IsIdentical(V, T) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
Vu := underlying(V)
|
||||||
|
Tu := underlying(T)
|
||||||
|
|
||||||
|
// x's type V and T have identical underlying types
|
||||||
|
// and at least one of V or T is not a named type
|
||||||
|
if IsIdentical(Vu, Tu) {
|
||||||
|
return !isNamed(V) || !isNamed(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
// T is an interface type and x implements T
|
||||||
|
if Ti, ok := Tu.(*Interface); ok {
|
||||||
|
if m, _ := missingMethod(x.typ, Ti); m == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// x is a bidirectional channel value, T is a channel
|
||||||
|
// type, x's type V and T have identical element types,
|
||||||
|
// and at least one of V or T is not a named type
|
||||||
|
if Vc, ok := Vu.(*Chan); ok && Vc.Dir == ast.SEND|ast.RECV {
|
||||||
|
if Tc, ok := Tu.(*Chan); ok && IsIdentical(Vc.Elt, Tc.Elt) {
|
||||||
|
return !isNamed(V) || !isNamed(T)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// x is the predeclared identifier nil and T is a pointer,
|
||||||
|
// function, slice, map, channel, or interface type
|
||||||
|
if x.isNil() {
|
||||||
|
switch t := Tu.(type) {
|
||||||
|
case *Basic:
|
||||||
|
if t.Kind == UnsafePointer {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case *Pointer, *Signature, *Slice, *Map, *Chan, *Interface:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// x is an untyped constant representable by a value of type T
|
||||||
|
// TODO(gri) This is borrowing from checker.convertUntyped and
|
||||||
|
// checker.isRepresentable. Need to clean up.
|
||||||
|
if isUntyped(Vu) {
|
||||||
|
switch t := Tu.(type) {
|
||||||
|
case *Basic:
|
||||||
|
if x.mode == constant {
|
||||||
|
return isRepresentableConst(x.val, ctxt, t.Kind)
|
||||||
|
}
|
||||||
|
// The result of a comparison is an untyped boolean,
|
||||||
|
// but may not be a constant.
|
||||||
|
if Vb, _ := Vu.(*Basic); Vb != nil {
|
||||||
|
return Vb.Kind == UntypedBool && isBoolean(Tu)
|
||||||
|
}
|
||||||
|
case *Interface:
|
||||||
|
return x.isNil() || len(t.Methods) == 0
|
||||||
|
case *Pointer, *Signature, *Slice, *Map, *Chan:
|
||||||
|
return x.isNil()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isInteger reports whether x is a (typed or untyped) integer value.
|
||||||
|
func (x *operand) isInteger() bool {
|
||||||
|
return x.mode == invalid ||
|
||||||
|
isInteger(x.typ) ||
|
||||||
|
x.mode == constant && isRepresentableConst(x.val, nil, UntypedInt) // no context required for UntypedInt
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookupResult represents the result of a struct field/method lookup.
|
||||||
|
type lookupResult struct {
|
||||||
|
mode operandMode
|
||||||
|
typ Type
|
||||||
|
index []int // field index sequence; nil for methods
|
||||||
|
}
|
||||||
|
|
||||||
|
type embeddedType struct {
|
||||||
|
typ *NamedType
|
||||||
|
index []int // field index sequence
|
||||||
|
multiples bool // if set, typ is embedded multiple times at the same level
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookupFieldBreadthFirst searches all types in list for a single entry (field
|
||||||
|
// or method) of the given name from the given package. If such a field is found,
|
||||||
|
// the result describes the field mode and type; otherwise the result mode is invalid.
|
||||||
|
// (This function is similar in structure to FieldByNameFunc in reflect/type.go)
|
||||||
|
//
|
||||||
|
func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res lookupResult) {
|
||||||
|
// visited records the types that have been searched already.
|
||||||
|
visited := make(map[*NamedType]bool)
|
||||||
|
|
||||||
|
// embedded types of the next lower level
|
||||||
|
var next []embeddedType
|
||||||
|
|
||||||
|
// potentialMatch is invoked every time a match is found.
|
||||||
|
potentialMatch := func(multiples bool, mode operandMode, typ Type) bool {
|
||||||
|
if multiples || res.mode != invalid {
|
||||||
|
// name appeared already at this level - annihilate
|
||||||
|
res.mode = invalid
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// first appearance of name
|
||||||
|
res.mode = mode
|
||||||
|
res.typ = typ
|
||||||
|
res.index = nil
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search the current level if there is any work to do and collect
|
||||||
|
// embedded types of the next lower level in the next list.
|
||||||
|
for len(list) > 0 {
|
||||||
|
// The res.mode indicates whether we have found a match already
|
||||||
|
// on this level (mode != invalid), or not (mode == invalid).
|
||||||
|
assert(res.mode == invalid)
|
||||||
|
|
||||||
|
// start with empty next list (don't waste underlying array)
|
||||||
|
next = next[:0]
|
||||||
|
|
||||||
|
// look for name in all types at this level
|
||||||
|
for _, e := range list {
|
||||||
|
typ := e.typ
|
||||||
|
if visited[typ] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
visited[typ] = true
|
||||||
|
|
||||||
|
// look for a matching attached method
|
||||||
|
for _, m := range typ.Methods {
|
||||||
|
if name.IsSame(m.QualifiedName) {
|
||||||
|
assert(m.Type != nil)
|
||||||
|
if !potentialMatch(e.multiples, value, m.Type) {
|
||||||
|
return // name collision
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t := typ.Underlying.(type) {
|
||||||
|
case *Struct:
|
||||||
|
// look for a matching field and collect embedded types
|
||||||
|
for i, f := range t.Fields {
|
||||||
|
if name.IsSame(f.QualifiedName) {
|
||||||
|
assert(f.Type != nil)
|
||||||
|
if !potentialMatch(e.multiples, variable, f.Type) {
|
||||||
|
return // name collision
|
||||||
|
}
|
||||||
|
var index []int
|
||||||
|
index = append(index, e.index...) // copy e.index
|
||||||
|
index = append(index, i)
|
||||||
|
res.index = index
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Collect embedded struct fields for searching the next
|
||||||
|
// lower level, but only if we have not seen a match yet
|
||||||
|
// (if we have a match it is either the desired field or
|
||||||
|
// we have a name collision on the same level; in either
|
||||||
|
// case we don't need to look further).
|
||||||
|
// Embedded fields are always of the form T or *T where
|
||||||
|
// T is a named type. If typ appeared multiple times at
|
||||||
|
// this level, f.Type appears multiple times at the next
|
||||||
|
// level.
|
||||||
|
if f.IsAnonymous && res.mode == invalid {
|
||||||
|
// Ignore embedded basic types - only user-defined
|
||||||
|
// named types can have methods or have struct fields.
|
||||||
|
if t, _ := deref(f.Type).(*NamedType); t != nil {
|
||||||
|
var index []int
|
||||||
|
index = append(index, e.index...) // copy e.index
|
||||||
|
index = append(index, i)
|
||||||
|
next = append(next, embeddedType{t, index, e.multiples})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Interface:
|
||||||
|
// look for a matching method
|
||||||
|
for _, m := range t.Methods {
|
||||||
|
if name.IsSame(m.QualifiedName) {
|
||||||
|
assert(m.Type != nil)
|
||||||
|
if !potentialMatch(e.multiples, value, m.Type) {
|
||||||
|
return // name collision
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.mode != invalid {
|
||||||
|
// we found a single match on this level
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// No match and no collision so far.
|
||||||
|
// Compute the list to search for the next level.
|
||||||
|
list = list[:0] // don't waste underlying array
|
||||||
|
for _, e := range next {
|
||||||
|
// Instead of adding the same type multiple times, look for
|
||||||
|
// it in the list and mark it as multiple if it was added
|
||||||
|
// before.
|
||||||
|
// We use a sequential search (instead of a map for next)
|
||||||
|
// because the lists tend to be small, can easily be reused,
|
||||||
|
// and explicit search appears to be faster in this case.
|
||||||
|
if alt := findType(list, e.typ); alt != nil {
|
||||||
|
alt.multiples = true
|
||||||
|
} else {
|
||||||
|
list = append(list, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func findType(list []embeddedType, typ *NamedType) *embeddedType {
|
||||||
|
for i := range list {
|
||||||
|
if p := &list[i]; p.typ == typ {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupField(typ Type, name QualifiedName) lookupResult {
|
||||||
|
typ = deref(typ)
|
||||||
|
|
||||||
|
if t, ok := typ.(*NamedType); ok {
|
||||||
|
for _, m := range t.Methods {
|
||||||
|
if name.IsSame(m.QualifiedName) {
|
||||||
|
assert(m.Type != nil)
|
||||||
|
return lookupResult{value, m.Type, nil}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typ = t.Underlying
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t := typ.(type) {
|
||||||
|
case *Struct:
|
||||||
|
var next []embeddedType
|
||||||
|
for i, f := range t.Fields {
|
||||||
|
if name.IsSame(f.QualifiedName) {
|
||||||
|
return lookupResult{variable, f.Type, []int{i}}
|
||||||
|
}
|
||||||
|
if f.IsAnonymous {
|
||||||
|
// Possible optimization: If the embedded type
|
||||||
|
// is a pointer to the current type we could
|
||||||
|
// ignore it.
|
||||||
|
// Ignore embedded basic types - only user-defined
|
||||||
|
// named types can have methods or have struct fields.
|
||||||
|
if t, _ := deref(f.Type).(*NamedType); t != nil {
|
||||||
|
next = append(next, embeddedType{t, []int{i}, false})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(next) > 0 {
|
||||||
|
return lookupFieldBreadthFirst(next, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Interface:
|
||||||
|
for _, m := range t.Methods {
|
||||||
|
if name.IsSame(m.QualifiedName) {
|
||||||
|
return lookupResult{value, m.Type, nil}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not found
|
||||||
|
return lookupResult{mode: invalid}
|
||||||
|
}
|
303
go/types/predicates.go
Normal file
303
go/types/predicates.go
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
// Copyright 2012 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 commonly used type predicates.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
func isNamed(typ Type) bool {
|
||||||
|
if _, ok := typ.(*Basic); ok {
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
_, ok := typ.(*NamedType)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBoolean(typ Type) bool {
|
||||||
|
t, ok := underlying(typ).(*Basic)
|
||||||
|
return ok && t.Info&IsBoolean != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isInteger(typ Type) bool {
|
||||||
|
t, ok := underlying(typ).(*Basic)
|
||||||
|
return ok && t.Info&IsInteger != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUnsigned(typ Type) bool {
|
||||||
|
t, ok := underlying(typ).(*Basic)
|
||||||
|
return ok && t.Info&IsUnsigned != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isFloat(typ Type) bool {
|
||||||
|
t, ok := underlying(typ).(*Basic)
|
||||||
|
return ok && t.Info&IsFloat != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isComplex(typ Type) bool {
|
||||||
|
t, ok := underlying(typ).(*Basic)
|
||||||
|
return ok && t.Info&IsComplex != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNumeric(typ Type) bool {
|
||||||
|
t, ok := underlying(typ).(*Basic)
|
||||||
|
return ok && t.Info&IsNumeric != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isString(typ Type) bool {
|
||||||
|
t, ok := underlying(typ).(*Basic)
|
||||||
|
return ok && t.Info&IsString != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUntyped(typ Type) bool {
|
||||||
|
t, ok := underlying(typ).(*Basic)
|
||||||
|
return ok && t.Info&IsUntyped != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isOrdered(typ Type) bool {
|
||||||
|
t, ok := underlying(typ).(*Basic)
|
||||||
|
return ok && t.Info&IsOrdered != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isConstType(typ Type) bool {
|
||||||
|
t, ok := underlying(typ).(*Basic)
|
||||||
|
return ok && t.Info&IsConstType != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isComparable(typ Type) bool {
|
||||||
|
switch t := underlying(typ).(type) {
|
||||||
|
case *Basic:
|
||||||
|
return t.Kind != Invalid && t.Kind != UntypedNil
|
||||||
|
case *Pointer, *Interface, *Chan:
|
||||||
|
// assumes types are equal for pointers and channels
|
||||||
|
return true
|
||||||
|
case *Struct:
|
||||||
|
for _, f := range t.Fields {
|
||||||
|
if !isComparable(f.Type) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case *Array:
|
||||||
|
return isComparable(t.Elt)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasNil(typ Type) bool {
|
||||||
|
switch underlying(typ).(type) {
|
||||||
|
case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsIdentical returns true if x and y are identical.
|
||||||
|
func IsIdentical(x, y Type) bool {
|
||||||
|
if x == y {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch x := x.(type) {
|
||||||
|
case *Basic:
|
||||||
|
// Basic types are singletons except for the rune and byte
|
||||||
|
// aliases, thus we cannot solely rely on the x == y check
|
||||||
|
// above.
|
||||||
|
if y, ok := y.(*Basic); ok {
|
||||||
|
return x.Kind == y.Kind
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Array:
|
||||||
|
// Two array types are identical if they have identical element types
|
||||||
|
// and the same array length.
|
||||||
|
if y, ok := y.(*Array); ok {
|
||||||
|
return x.Len == y.Len && IsIdentical(x.Elt, y.Elt)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Slice:
|
||||||
|
// Two slice types are identical if they have identical element types.
|
||||||
|
if y, ok := y.(*Slice); ok {
|
||||||
|
return IsIdentical(x.Elt, y.Elt)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Struct:
|
||||||
|
// Two struct types are identical if they have the same sequence of fields,
|
||||||
|
// and if corresponding fields have the same names, and identical types,
|
||||||
|
// and identical tags. Two anonymous fields are considered to have the same
|
||||||
|
// name. Lower-case field names from different packages are always different.
|
||||||
|
if y, ok := y.(*Struct); ok {
|
||||||
|
if len(x.Fields) == len(y.Fields) {
|
||||||
|
for i, f := range x.Fields {
|
||||||
|
g := y.Fields[i]
|
||||||
|
if !f.QualifiedName.IsSame(g.QualifiedName) ||
|
||||||
|
!IsIdentical(f.Type, g.Type) ||
|
||||||
|
f.Tag != g.Tag ||
|
||||||
|
f.IsAnonymous != g.IsAnonymous {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Pointer:
|
||||||
|
// Two pointer types are identical if they have identical base types.
|
||||||
|
if y, ok := y.(*Pointer); ok {
|
||||||
|
return IsIdentical(x.Base, y.Base)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Signature:
|
||||||
|
// Two function types are identical if they have the same number of parameters
|
||||||
|
// and result values, corresponding parameter and result types are identical,
|
||||||
|
// and either both functions are variadic or neither is. Parameter and result
|
||||||
|
// names are not required to match.
|
||||||
|
if y, ok := y.(*Signature); ok {
|
||||||
|
return identicalTypes(x.Params, y.Params) &&
|
||||||
|
identicalTypes(x.Results, y.Results) &&
|
||||||
|
x.IsVariadic == y.IsVariadic
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Interface:
|
||||||
|
// Two interface types are identical if they have the same set of methods with
|
||||||
|
// the same names and identical function types. Lower-case method names from
|
||||||
|
// different packages are always different. The order of the methods is irrelevant.
|
||||||
|
if y, ok := y.(*Interface); ok {
|
||||||
|
return identicalMethods(x.Methods, y.Methods) // methods are sorted
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Map:
|
||||||
|
// Two map types are identical if they have identical key and value types.
|
||||||
|
if y, ok := y.(*Map); ok {
|
||||||
|
return IsIdentical(x.Key, y.Key) && IsIdentical(x.Elt, y.Elt)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Chan:
|
||||||
|
// Two channel types are identical if they have identical value types
|
||||||
|
// and the same direction.
|
||||||
|
if y, ok := y.(*Chan); ok {
|
||||||
|
return x.Dir == y.Dir && IsIdentical(x.Elt, y.Elt)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *NamedType:
|
||||||
|
// Two named types are identical if their type names originate
|
||||||
|
// in the same type declaration.
|
||||||
|
if y, ok := y.(*NamedType); ok {
|
||||||
|
return x.Obj == y.Obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// identicalTypes returns true if both lists a and b have the
|
||||||
|
// same length and corresponding objects have identical types.
|
||||||
|
func identicalTypes(a, b []*Var) bool {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, x := range a {
|
||||||
|
y := b[i]
|
||||||
|
if !IsIdentical(x.Type, y.Type) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// identicalMethods returns true if both lists a and b have the
|
||||||
|
// same length and corresponding methods have identical types.
|
||||||
|
// TODO(gri) make this more efficient
|
||||||
|
func identicalMethods(a, b []*Method) bool {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
m := make(map[QualifiedName]*Method)
|
||||||
|
for _, x := range a {
|
||||||
|
assert(m[x.QualifiedName] == nil) // method list must not have duplicate entries
|
||||||
|
m[x.QualifiedName] = x
|
||||||
|
}
|
||||||
|
for _, y := range b {
|
||||||
|
if x := m[y.QualifiedName]; x == nil || !IsIdentical(x.Type, y.Type) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// underlying returns the underlying type of typ.
|
||||||
|
func underlying(typ Type) Type {
|
||||||
|
// Basic types are representing themselves directly even though they are named.
|
||||||
|
if typ, ok := typ.(*NamedType); ok {
|
||||||
|
return typ.Underlying // underlying types are never NamedTypes
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// deref returns a pointer's base type; otherwise it returns typ.
|
||||||
|
func deref(typ Type) Type {
|
||||||
|
if typ, ok := underlying(typ).(*Pointer); ok {
|
||||||
|
return typ.Base
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultType returns the default "typed" type for an "untyped" type;
|
||||||
|
// it returns the incoming type for all other types. If there is no
|
||||||
|
// corresponding untyped type, the result is Typ[Invalid].
|
||||||
|
//
|
||||||
|
func defaultType(typ Type) Type {
|
||||||
|
if t, ok := typ.(*Basic); ok {
|
||||||
|
k := Invalid
|
||||||
|
switch t.Kind {
|
||||||
|
// case UntypedNil:
|
||||||
|
// There is no default type for nil. For a good error message,
|
||||||
|
// catch this case before calling this function.
|
||||||
|
case UntypedBool:
|
||||||
|
k = Bool
|
||||||
|
case UntypedInt:
|
||||||
|
k = Int
|
||||||
|
case UntypedRune:
|
||||||
|
k = Rune
|
||||||
|
case UntypedFloat:
|
||||||
|
k = Float64
|
||||||
|
case UntypedComplex:
|
||||||
|
k = Complex128
|
||||||
|
case UntypedString:
|
||||||
|
k = String
|
||||||
|
}
|
||||||
|
typ = Typ[k]
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// missingMethod returns (nil, false) if typ implements T, otherwise
|
||||||
|
// it returns the first missing method required by T and whether it
|
||||||
|
// is missing or simply has the wrong type.
|
||||||
|
//
|
||||||
|
func missingMethod(typ Type, T *Interface) (method *Method, wrongType bool) {
|
||||||
|
// TODO(gri): this needs to correctly compare method names (taking package into account)
|
||||||
|
// TODO(gri): distinguish pointer and non-pointer receivers
|
||||||
|
// an interface type implements T if it has no methods with conflicting signatures
|
||||||
|
// Note: This is stronger than the current spec. Should the spec require this?
|
||||||
|
if ityp, _ := underlying(typ).(*Interface); ityp != nil {
|
||||||
|
for _, m := range T.Methods {
|
||||||
|
res := lookupField(ityp, m.QualifiedName) // TODO(gri) no need to go via lookupField
|
||||||
|
if res.mode != invalid && !IsIdentical(res.typ, m.Type) {
|
||||||
|
return m, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// a concrete type implements T if it implements all methods of T.
|
||||||
|
for _, m := range T.Methods {
|
||||||
|
res := lookupField(typ, m.QualifiedName)
|
||||||
|
if res.mode == invalid {
|
||||||
|
return m, false
|
||||||
|
}
|
||||||
|
if !IsIdentical(res.typ, m.Type) {
|
||||||
|
return m, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
197
go/types/resolve.go
Normal file
197
go/types/resolve.go
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
// Copyright 2013 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 (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (check *checker) declareObj(scope, altScope *Scope, obj Object, dotImport token.Pos) {
|
||||||
|
alt := scope.Insert(obj)
|
||||||
|
if alt == nil && altScope != nil {
|
||||||
|
// see if there is a conflicting declaration in altScope
|
||||||
|
alt = altScope.Lookup(obj.GetName())
|
||||||
|
}
|
||||||
|
if alt != nil {
|
||||||
|
prevDecl := ""
|
||||||
|
|
||||||
|
// for dot-imports, local declarations are declared first - swap messages
|
||||||
|
if dotImport.IsValid() {
|
||||||
|
if pos := alt.GetPos(); pos.IsValid() {
|
||||||
|
check.errorf(pos, fmt.Sprintf("%s redeclared in this block by dot-import at %s",
|
||||||
|
obj.GetName(), check.fset.Position(dotImport)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get by w/o other position
|
||||||
|
check.errorf(dotImport, fmt.Sprintf("dot-import redeclares %s", obj.GetName()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if pos := alt.GetPos(); pos.IsValid() {
|
||||||
|
prevDecl = fmt.Sprintf("\n\tother declaration at %s", check.fset.Position(pos))
|
||||||
|
}
|
||||||
|
check.errorf(obj.GetPos(), fmt.Sprintf("%s redeclared in this block%s", obj.GetName(), prevDecl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) resolveIdent(scope *Scope, ident *ast.Ident) bool {
|
||||||
|
for ; scope != nil; scope = scope.Outer {
|
||||||
|
if obj := scope.Lookup(ident.Name); obj != nil {
|
||||||
|
check.register(ident, obj)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) {
|
||||||
|
pkg := &Package{Scope: &Scope{Outer: Universe}, Imports: make(map[string]*Package)}
|
||||||
|
check.pkg = pkg
|
||||||
|
|
||||||
|
// complete package scope
|
||||||
|
i := 0
|
||||||
|
for _, file := range check.files {
|
||||||
|
// package names must match
|
||||||
|
switch name := file.Name.Name; {
|
||||||
|
case pkg.Name == "":
|
||||||
|
pkg.Name = name
|
||||||
|
case name != pkg.Name:
|
||||||
|
check.errorf(file.Package, "package %s; expected %s", name, pkg.Name)
|
||||||
|
continue // ignore this file
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep this file
|
||||||
|
check.files[i] = file
|
||||||
|
i++
|
||||||
|
|
||||||
|
// the package identifier denotes the current package
|
||||||
|
check.register(file.Name, pkg)
|
||||||
|
|
||||||
|
// insert top-level file objects in package scope
|
||||||
|
// (the parser took care of declaration errors)
|
||||||
|
for _, decl := range file.Decls {
|
||||||
|
switch d := decl.(type) {
|
||||||
|
case *ast.BadDecl:
|
||||||
|
// ignore
|
||||||
|
case *ast.GenDecl:
|
||||||
|
if d.Tok == token.CONST {
|
||||||
|
check.assocInitvals(d)
|
||||||
|
}
|
||||||
|
for _, spec := range d.Specs {
|
||||||
|
switch s := spec.(type) {
|
||||||
|
case *ast.ImportSpec:
|
||||||
|
// handled separately below
|
||||||
|
case *ast.ValueSpec:
|
||||||
|
for _, name := range s.Names {
|
||||||
|
if name.Name == "_" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pkg.Scope.Insert(check.lookup(name))
|
||||||
|
}
|
||||||
|
case *ast.TypeSpec:
|
||||||
|
if s.Name.Name == "_" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pkg.Scope.Insert(check.lookup(s.Name))
|
||||||
|
default:
|
||||||
|
check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
if d.Recv != nil {
|
||||||
|
// collect method
|
||||||
|
methods = append(methods, d)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if d.Name.Name == "_" || d.Name.Name == "init" {
|
||||||
|
continue // blank (_) and init functions are inaccessible
|
||||||
|
}
|
||||||
|
pkg.Scope.Insert(check.lookup(d.Name))
|
||||||
|
default:
|
||||||
|
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check.files = check.files[0:i]
|
||||||
|
|
||||||
|
// complete file scopes with imports and resolve identifiers
|
||||||
|
for _, file := range check.files {
|
||||||
|
// build file scope by processing all imports
|
||||||
|
importErrors := false
|
||||||
|
fileScope := &Scope{Outer: pkg.Scope}
|
||||||
|
for _, spec := range file.Imports {
|
||||||
|
if importer == nil {
|
||||||
|
importErrors = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
path, _ := strconv.Unquote(spec.Path.Value)
|
||||||
|
imp, err := importer(pkg.Imports, path)
|
||||||
|
if err != nil {
|
||||||
|
check.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err)
|
||||||
|
importErrors = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// TODO(gri) If a local package name != "." is provided,
|
||||||
|
// global identifier resolution could proceed even if the
|
||||||
|
// import failed. Consider adjusting the logic here a bit.
|
||||||
|
|
||||||
|
// local name overrides imported package name
|
||||||
|
name := imp.Name
|
||||||
|
if spec.Name != nil {
|
||||||
|
name = spec.Name.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// add import to file scope
|
||||||
|
if name == "." {
|
||||||
|
// merge imported scope with file scope
|
||||||
|
for _, obj := range imp.Scope.Entries {
|
||||||
|
// gcimported package scopes contain non-exported
|
||||||
|
// objects such as types used in partially exported
|
||||||
|
// objects - do not accept them
|
||||||
|
if ast.IsExported(obj.GetName()) {
|
||||||
|
check.declareObj(fileScope, pkg.Scope, obj, spec.Pos())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO(gri) consider registering the "." identifier
|
||||||
|
// if we have Context.Ident callbacks for say blank
|
||||||
|
// (_) identifiers
|
||||||
|
// check.register(spec.Name, pkg)
|
||||||
|
} else if name != "_" {
|
||||||
|
// declare imported package object in file scope
|
||||||
|
// (do not re-use imp in the file scope but create
|
||||||
|
// a new object instead; the Decl field is different
|
||||||
|
// for different files)
|
||||||
|
obj := &Package{Name: name, Scope: imp.Scope, spec: spec}
|
||||||
|
check.declareObj(fileScope, pkg.Scope, obj, token.NoPos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve identifiers
|
||||||
|
if importErrors {
|
||||||
|
// don't use the universe scope without correct imports
|
||||||
|
// (objects in the universe may be shadowed by imports;
|
||||||
|
// with missing imports, identifiers might get resolved
|
||||||
|
// incorrectly to universe objects)
|
||||||
|
pkg.Scope.Outer = nil
|
||||||
|
}
|
||||||
|
i := 0
|
||||||
|
for _, ident := range file.Unresolved {
|
||||||
|
if !check.resolveIdent(fileScope, ident) {
|
||||||
|
check.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
|
||||||
|
file.Unresolved[i] = ident
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
file.Unresolved = file.Unresolved[0:i]
|
||||||
|
pkg.Scope.Outer = Universe // reset outer scope (is nil if there were importErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
167
go/types/resolver_test.go
Normal file
167
go/types/resolver_test.go
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
// Copyright 2011 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 (
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sources = []string{
|
||||||
|
`
|
||||||
|
package p
|
||||||
|
import "fmt"
|
||||||
|
import "math"
|
||||||
|
const pi = math.Pi
|
||||||
|
func sin(x float64) float64 {
|
||||||
|
return math.Sin(x)
|
||||||
|
}
|
||||||
|
var Println = fmt.Println
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
package p
|
||||||
|
import "fmt"
|
||||||
|
func f() string {
|
||||||
|
_ = "foo"
|
||||||
|
return fmt.Sprintf("%d", g())
|
||||||
|
}
|
||||||
|
func g() (x int) { return }
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
package p
|
||||||
|
import . "go/parser"
|
||||||
|
import "sync"
|
||||||
|
func g() Mode { return ImportsOnly }
|
||||||
|
var _, x int = 1, 2
|
||||||
|
func init() {}
|
||||||
|
type T struct{ sync.Mutex; a, b, c int}
|
||||||
|
type I interface{ m() }
|
||||||
|
var _ = T{a: 1, b: 2, c: 3}
|
||||||
|
func (_ T) m() {}
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
var pkgnames = []string{
|
||||||
|
"fmt",
|
||||||
|
"math",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveQualifiedIdents(t *testing.T) {
|
||||||
|
// parse package files
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
var files []*ast.File
|
||||||
|
for _, src := range sources {
|
||||||
|
f, err := parser.ParseFile(fset, "", src, parser.DeclarationErrors)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
files = append(files, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve and type-check package AST
|
||||||
|
idents := make(map[*ast.Ident]Object)
|
||||||
|
var ctxt Context
|
||||||
|
ctxt.Ident = func(id *ast.Ident, obj Object) { idents[id] = obj }
|
||||||
|
pkg, err := ctxt.Check(fset, files)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that all packages were imported
|
||||||
|
for _, name := range pkgnames {
|
||||||
|
if pkg.Imports[name] == nil {
|
||||||
|
t.Errorf("package %s not imported", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that there are no top-level unresolved identifiers
|
||||||
|
for _, f := range files {
|
||||||
|
for _, x := range f.Unresolved {
|
||||||
|
t.Errorf("%s: unresolved global identifier %s", fset.Position(x.Pos()), x.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that qualified identifiers are resolved
|
||||||
|
for _, f := range files {
|
||||||
|
ast.Inspect(f, func(n ast.Node) bool {
|
||||||
|
if s, ok := n.(*ast.SelectorExpr); ok {
|
||||||
|
if x, ok := s.X.(*ast.Ident); ok {
|
||||||
|
obj := idents[x]
|
||||||
|
if obj == nil {
|
||||||
|
t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, ok := obj.(*Package); ok && idents[s.Sel] == nil {
|
||||||
|
t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently, the Check API doesn't call Ident for fields, methods, and composite literal keys.
|
||||||
|
// Introduce them artifically so that we can run the check below.
|
||||||
|
for _, f := range files {
|
||||||
|
ast.Inspect(f, func(n ast.Node) bool {
|
||||||
|
switch x := n.(type) {
|
||||||
|
case *ast.StructType:
|
||||||
|
for _, list := range x.Fields.List {
|
||||||
|
for _, f := range list.Names {
|
||||||
|
assert(idents[f] == nil)
|
||||||
|
idents[f] = &Var{Pkg: pkg, Name: f.Name}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ast.InterfaceType:
|
||||||
|
for _, list := range x.Methods.List {
|
||||||
|
for _, f := range list.Names {
|
||||||
|
assert(idents[f] == nil)
|
||||||
|
idents[f] = &Func{Pkg: pkg, Name: f.Name}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ast.CompositeLit:
|
||||||
|
for _, e := range x.Elts {
|
||||||
|
if kv, ok := e.(*ast.KeyValueExpr); ok {
|
||||||
|
if k, ok := kv.Key.(*ast.Ident); ok {
|
||||||
|
assert(idents[k] == nil)
|
||||||
|
idents[k] = &Var{Pkg: pkg, Name: k.Name}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that each identifier in the source is enumerated by the Context.Ident callback
|
||||||
|
for _, f := range files {
|
||||||
|
ast.Inspect(f, func(n ast.Node) bool {
|
||||||
|
if x, ok := n.(*ast.Ident); ok && x.Name != "_" && x.Name != "." {
|
||||||
|
obj := idents[x]
|
||||||
|
if obj == nil {
|
||||||
|
t.Errorf("%s: unresolved identifier %s", fset.Position(x.Pos()), x.Name)
|
||||||
|
} else {
|
||||||
|
delete(idents, x)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) enable code below
|
||||||
|
// At the moment, the type checker introduces artifical identifiers which are not
|
||||||
|
// present in the source. Once it doesn't do that anymore, enable the checks below.
|
||||||
|
/*
|
||||||
|
for x := range idents {
|
||||||
|
t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
189
go/types/return.go
Normal file
189
go/types/return.go
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
// Copyright 2013 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 isTerminating.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// isTerminating reports if s is a terminating statement.
|
||||||
|
// If s is labeled, label is the label name; otherwise s
|
||||||
|
// is "".
|
||||||
|
func (check *checker) isTerminating(s ast.Stmt, label string) bool {
|
||||||
|
switch s := s.(type) {
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
|
||||||
|
case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.SendStmt,
|
||||||
|
*ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt, *ast.DeferStmt,
|
||||||
|
*ast.RangeStmt:
|
||||||
|
// no chance
|
||||||
|
|
||||||
|
case *ast.LabeledStmt:
|
||||||
|
return check.isTerminating(s.Stmt, s.Label.Name)
|
||||||
|
|
||||||
|
case *ast.ExprStmt:
|
||||||
|
// the predeclared panic() function is terminating
|
||||||
|
if call, _ := s.X.(*ast.CallExpr); call != nil {
|
||||||
|
if id, _ := call.Fun.(*ast.Ident); id != nil {
|
||||||
|
if obj := check.lookup(id); obj != nil {
|
||||||
|
// TODO(gri) Predeclared functions should be modelled as objects
|
||||||
|
// rather then ordinary functions that have a predeclared
|
||||||
|
// function type. This would simplify code here and else-
|
||||||
|
// where.
|
||||||
|
if f, _ := obj.(*Func); f != nil && f.Type == predeclaredFunctions[_Panic] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ReturnStmt:
|
||||||
|
return true
|
||||||
|
|
||||||
|
case *ast.BranchStmt:
|
||||||
|
if s.Tok == token.GOTO || s.Tok == token.FALLTHROUGH {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.BlockStmt:
|
||||||
|
return check.isTerminatingList(s.List, "")
|
||||||
|
|
||||||
|
case *ast.IfStmt:
|
||||||
|
if s.Else != nil &&
|
||||||
|
check.isTerminating(s.Body, "") &&
|
||||||
|
check.isTerminating(s.Else, "") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.SwitchStmt:
|
||||||
|
return check.isTerminatingSwitch(s.Body, label)
|
||||||
|
|
||||||
|
case *ast.TypeSwitchStmt:
|
||||||
|
return check.isTerminatingSwitch(s.Body, label)
|
||||||
|
|
||||||
|
case *ast.SelectStmt:
|
||||||
|
for _, s := range s.Body.List {
|
||||||
|
cc := s.(*ast.CommClause)
|
||||||
|
if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
|
||||||
|
case *ast.ForStmt:
|
||||||
|
if s.Cond == nil && !hasBreak(s.Body, label, true) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) isTerminatingList(list []ast.Stmt, label string) bool {
|
||||||
|
n := len(list)
|
||||||
|
return n > 0 && check.isTerminating(list[n-1], label)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) isTerminatingSwitch(body *ast.BlockStmt, label string) bool {
|
||||||
|
hasDefault := false
|
||||||
|
for _, s := range body.List {
|
||||||
|
cc := s.(*ast.CaseClause)
|
||||||
|
if cc.List == nil {
|
||||||
|
hasDefault = true
|
||||||
|
}
|
||||||
|
if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) For nested breakable statements, the current implementation of hasBreak
|
||||||
|
// will traverse the same subtree repeatedly, once for each label. Replace
|
||||||
|
// with a single-pass label/break matching phase.
|
||||||
|
|
||||||
|
// hasBreak reports if s is or contains a break statement
|
||||||
|
// referring to the label-ed statement or implicit-ly the
|
||||||
|
// closest outer breakable statement.
|
||||||
|
func hasBreak(s ast.Stmt, label string, implicit bool) bool {
|
||||||
|
switch s := s.(type) {
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
|
||||||
|
case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.ExprStmt,
|
||||||
|
*ast.SendStmt, *ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt,
|
||||||
|
*ast.DeferStmt, *ast.ReturnStmt:
|
||||||
|
// no chance
|
||||||
|
|
||||||
|
case *ast.LabeledStmt:
|
||||||
|
return hasBreak(s.Stmt, label, implicit)
|
||||||
|
|
||||||
|
case *ast.BranchStmt:
|
||||||
|
if s.Tok == token.BREAK {
|
||||||
|
if s.Label == nil {
|
||||||
|
return implicit
|
||||||
|
}
|
||||||
|
if s.Label.Name == label {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.BlockStmt:
|
||||||
|
return hasBreakList(s.List, label, implicit)
|
||||||
|
|
||||||
|
case *ast.IfStmt:
|
||||||
|
if hasBreak(s.Body, label, implicit) ||
|
||||||
|
s.Else != nil && hasBreak(s.Else, label, implicit) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.CaseClause:
|
||||||
|
return hasBreakList(s.Body, label, implicit)
|
||||||
|
|
||||||
|
case *ast.SwitchStmt:
|
||||||
|
if label != "" && hasBreak(s.Body, label, false) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.TypeSwitchStmt:
|
||||||
|
if label != "" && hasBreak(s.Body, label, false) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.CommClause:
|
||||||
|
return hasBreakList(s.Body, label, implicit)
|
||||||
|
|
||||||
|
case *ast.SelectStmt:
|
||||||
|
if label != "" && hasBreak(s.Body, label, false) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ForStmt:
|
||||||
|
if label != "" && hasBreak(s.Body, label, false) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.RangeStmt:
|
||||||
|
if label != "" && hasBreak(s.Body, label, false) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasBreakList(list []ast.Stmt, label string, implicit bool) bool {
|
||||||
|
for _, s := range list {
|
||||||
|
if hasBreak(s, label, implicit) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
78
go/types/scope.go
Normal file
78
go/types/scope.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// Copyright 2013 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"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Scope maintains the set of named language entities declared
|
||||||
|
// in the scope and a link to the immediately surrounding (outer)
|
||||||
|
// scope.
|
||||||
|
//
|
||||||
|
type Scope struct {
|
||||||
|
Outer *Scope
|
||||||
|
Entries []Object // scope entries in insertion order
|
||||||
|
large map[string]Object // for fast lookup - only used for larger scopes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup returns the object with the given name if it is
|
||||||
|
// found in scope s, otherwise it returns nil. Outer scopes
|
||||||
|
// are ignored.
|
||||||
|
//
|
||||||
|
func (s *Scope) Lookup(name string) Object {
|
||||||
|
if s.large != nil {
|
||||||
|
return s.large[name]
|
||||||
|
}
|
||||||
|
for _, obj := range s.Entries {
|
||||||
|
if obj.GetName() == name {
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert attempts to insert an object obj into scope s.
|
||||||
|
// If s already contains an object with the same name,
|
||||||
|
// Insert leaves s unchanged and returns that object.
|
||||||
|
// Otherwise it inserts obj and returns nil.
|
||||||
|
//
|
||||||
|
func (s *Scope) Insert(obj Object) Object {
|
||||||
|
name := obj.GetName()
|
||||||
|
if alt := s.Lookup(name); alt != nil {
|
||||||
|
return alt
|
||||||
|
}
|
||||||
|
s.Entries = append(s.Entries, obj)
|
||||||
|
|
||||||
|
// If the scope size reaches a threshold, use a map for faster lookups.
|
||||||
|
const threshold = 20
|
||||||
|
if len(s.Entries) > threshold {
|
||||||
|
if s.large == nil {
|
||||||
|
m := make(map[string]Object, len(s.Entries))
|
||||||
|
for _, obj := range s.Entries {
|
||||||
|
m[obj.GetName()] = obj
|
||||||
|
}
|
||||||
|
s.large = m
|
||||||
|
}
|
||||||
|
s.large[name] = obj
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugging support
|
||||||
|
func (s *Scope) String() string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
fmt.Fprintf(&buf, "scope %p {", s)
|
||||||
|
if s != nil && len(s.Entries) > 0 {
|
||||||
|
fmt.Fprintln(&buf)
|
||||||
|
for _, obj := range s.Entries {
|
||||||
|
fmt.Fprintf(&buf, "\t%s\t%T\n", obj.GetName(), obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&buf, "}\n")
|
||||||
|
return buf.String()
|
||||||
|
}
|
162
go/types/sizes.go
Normal file
162
go/types/sizes.go
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
// Copyright 2013 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 support for (unsafe) Alignof, Offsetof, and Sizeof.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
func (ctxt *Context) alignof(typ Type) int64 {
|
||||||
|
if f := ctxt.Alignof; f != nil {
|
||||||
|
if a := f(typ); a >= 1 {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
panic("Context.Alignof returned an alignment < 1")
|
||||||
|
}
|
||||||
|
return DefaultAlignof(typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctxt *Context) offsetsof(s *Struct) []int64 {
|
||||||
|
offsets := s.offsets
|
||||||
|
if offsets == nil {
|
||||||
|
// compute offsets on demand
|
||||||
|
if f := ctxt.Offsetsof; f != nil {
|
||||||
|
offsets = f(s.Fields)
|
||||||
|
// sanity checks
|
||||||
|
if len(offsets) != len(s.Fields) {
|
||||||
|
panic("Context.Offsetsof returned the wrong number of offsets")
|
||||||
|
}
|
||||||
|
for _, o := range offsets {
|
||||||
|
if o < 0 {
|
||||||
|
panic("Context.Offsetsof returned an offset < 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
offsets = DefaultOffsetsof(s.Fields)
|
||||||
|
}
|
||||||
|
s.offsets = offsets
|
||||||
|
}
|
||||||
|
return offsets
|
||||||
|
}
|
||||||
|
|
||||||
|
// offsetof returns the offset of the field specified via
|
||||||
|
// the index sequence relative to typ. It returns a value
|
||||||
|
// < 0 if the field is in an embedded pointer type.
|
||||||
|
func (ctxt *Context) offsetof(typ Type, index []int) int64 {
|
||||||
|
var o int64
|
||||||
|
for _, i := range index {
|
||||||
|
s, _ := underlying(typ).(*Struct)
|
||||||
|
if s == nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
o += ctxt.offsetsof(s)[i]
|
||||||
|
typ = s.Fields[i].Type
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctxt *Context) sizeof(typ Type) int64 {
|
||||||
|
if f := ctxt.Sizeof; f != nil {
|
||||||
|
if s := f(typ); s >= 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
panic("Context.Sizeof returned a size < 0")
|
||||||
|
}
|
||||||
|
return DefaultSizeof(typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultMaxAlign is the default maximum alignment, in bytes,
|
||||||
|
// used by DefaultAlignof.
|
||||||
|
const DefaultMaxAlign = 8
|
||||||
|
|
||||||
|
// DefaultAlignof implements the default alignment computation
|
||||||
|
// for unsafe.Alignof. It is used if Context.Alignof == nil.
|
||||||
|
func DefaultAlignof(typ Type) int64 {
|
||||||
|
// For arrays and structs, alignment is defined in terms
|
||||||
|
// of alignment of the elements and fields, respectively.
|
||||||
|
switch t := underlying(typ).(type) {
|
||||||
|
case *Array:
|
||||||
|
// spec: "For a variable x of array type: unsafe.Alignof(x)
|
||||||
|
// is the same as unsafe.Alignof(x[0]), but at least 1."
|
||||||
|
return DefaultAlignof(t.Elt)
|
||||||
|
case *Struct:
|
||||||
|
// spec: "For a variable x of struct type: unsafe.Alignof(x)
|
||||||
|
// is the largest of the values unsafe.Alignof(x.f) for each
|
||||||
|
// field f of x, but at least 1."
|
||||||
|
max := int64(1)
|
||||||
|
for _, f := range t.Fields {
|
||||||
|
if a := DefaultAlignof(f.Type); a > max {
|
||||||
|
max = a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
a := DefaultSizeof(typ) // may be 0
|
||||||
|
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
|
||||||
|
if a < 1 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if a > DefaultMaxAlign {
|
||||||
|
return DefaultMaxAlign
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// align returns the smallest y >= x such that y % a == 0.
|
||||||
|
func align(x, a int64) int64 {
|
||||||
|
y := x + a - 1
|
||||||
|
return y - y%a
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultOffsetsof implements the default field offset computation
|
||||||
|
// for unsafe.Offsetof. It is used if Context.Offsetsof == nil.
|
||||||
|
func DefaultOffsetsof(fields []*Field) []int64 {
|
||||||
|
offsets := make([]int64, len(fields))
|
||||||
|
var o int64
|
||||||
|
for i, f := range fields {
|
||||||
|
a := DefaultAlignof(f.Type)
|
||||||
|
o = align(o, a)
|
||||||
|
offsets[i] = o
|
||||||
|
o += DefaultSizeof(f.Type)
|
||||||
|
}
|
||||||
|
return offsets
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultPtrSize is the default size of ints, uint, and pointers, in bytes,
|
||||||
|
// used by DefaultSizeof.
|
||||||
|
const DefaultPtrSize = 8
|
||||||
|
|
||||||
|
// DefaultSizeof implements the default size computation
|
||||||
|
// for unsafe.Sizeof. It is used if Context.Sizeof == nil.
|
||||||
|
func DefaultSizeof(typ Type) int64 {
|
||||||
|
switch t := underlying(typ).(type) {
|
||||||
|
case *Basic:
|
||||||
|
if s := t.size; s > 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
if t.Kind == String {
|
||||||
|
return DefaultPtrSize * 2
|
||||||
|
}
|
||||||
|
case *Array:
|
||||||
|
a := DefaultAlignof(t.Elt)
|
||||||
|
s := DefaultSizeof(t.Elt)
|
||||||
|
return align(s, a) * t.Len // may be 0
|
||||||
|
case *Slice:
|
||||||
|
return DefaultPtrSize * 3
|
||||||
|
case *Struct:
|
||||||
|
n := len(t.Fields)
|
||||||
|
if n == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
offsets := t.offsets
|
||||||
|
if t.offsets == nil {
|
||||||
|
// compute offsets on demand
|
||||||
|
offsets = DefaultOffsetsof(t.Fields)
|
||||||
|
t.offsets = offsets
|
||||||
|
}
|
||||||
|
return offsets[n-1] + DefaultSizeof(t.Fields[n-1].Type)
|
||||||
|
case *Signature:
|
||||||
|
return DefaultPtrSize * 2
|
||||||
|
}
|
||||||
|
return DefaultPtrSize // catch-all
|
||||||
|
}
|
133
go/types/stdlib_test.go
Normal file
133
go/types/stdlib_test.go
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
// Copyright 2013 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 tests types.Check by using it to
|
||||||
|
// typecheck the standard library.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
|
"go/parser"
|
||||||
|
"go/scanner"
|
||||||
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var verbose = flag.Bool("types.v", false, "verbose mode")
|
||||||
|
|
||||||
|
var (
|
||||||
|
pkgCount int // number of packages processed
|
||||||
|
start = time.Now()
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStdlib(t *testing.T) {
|
||||||
|
walkDirs(t, filepath.Join(runtime.GOROOT(), "src/pkg"))
|
||||||
|
if *verbose {
|
||||||
|
fmt.Println(pkgCount, "packages typechecked in", time.Since(start))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package paths of excluded packages.
|
||||||
|
var excluded = map[string]bool{
|
||||||
|
"builtin": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// typecheck typechecks the given package files.
|
||||||
|
func typecheck(t *testing.T, filenames []string) {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
|
||||||
|
// parse package files
|
||||||
|
var files []*ast.File
|
||||||
|
for _, filename := range filenames {
|
||||||
|
file, err := parser.ParseFile(fset, filename, nil, parser.DeclarationErrors|parser.AllErrors)
|
||||||
|
if err != nil {
|
||||||
|
// the parser error may be a list of individual errors; report them all
|
||||||
|
if list, ok := err.(scanner.ErrorList); ok {
|
||||||
|
for _, err := range list {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if *verbose {
|
||||||
|
if len(files) == 0 {
|
||||||
|
fmt.Println("package", file.Name.Name)
|
||||||
|
}
|
||||||
|
fmt.Println("\t", filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
files = append(files, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// typecheck package files
|
||||||
|
ctxt := Context{
|
||||||
|
Error: func(err error) { t.Error(err) },
|
||||||
|
}
|
||||||
|
ctxt.Check(fset, files)
|
||||||
|
pkgCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
// pkgfiles returns the list of package files for the given directory.
|
||||||
|
func pkgfiles(t *testing.T, dir string) []string {
|
||||||
|
ctxt := build.Default
|
||||||
|
ctxt.CgoEnabled = false
|
||||||
|
pkg, err := ctxt.ImportDir(dir, 0)
|
||||||
|
if err != nil {
|
||||||
|
if _, nogo := err.(*build.NoGoError); !nogo {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if excluded[pkg.ImportPath] {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var filenames []string
|
||||||
|
for _, name := range pkg.GoFiles {
|
||||||
|
filenames = append(filenames, filepath.Join(pkg.Dir, name))
|
||||||
|
}
|
||||||
|
for _, name := range pkg.TestGoFiles {
|
||||||
|
filenames = append(filenames, filepath.Join(pkg.Dir, name))
|
||||||
|
}
|
||||||
|
return filenames
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Could use filepath.Walk instead of walkDirs but that wouldn't
|
||||||
|
// necessarily be shorter or clearer after adding the code to
|
||||||
|
// terminate early for -short tests.
|
||||||
|
|
||||||
|
func walkDirs(t *testing.T, dir string) {
|
||||||
|
// limit run time for short tests
|
||||||
|
if testing.Short() && time.Since(start) >= 750*time.Millisecond {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fis, err := ioutil.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// typecheck package in directory
|
||||||
|
if files := pkgfiles(t, dir); files != nil {
|
||||||
|
typecheck(t, files)
|
||||||
|
}
|
||||||
|
|
||||||
|
// traverse subdirectories, but don't walk into testdata
|
||||||
|
for _, fi := range fis {
|
||||||
|
if fi.IsDir() && fi.Name() != "testdata" {
|
||||||
|
walkDirs(t, filepath.Join(dir, fi.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
724
go/types/stmt.go
Normal file
724
go/types/stmt.go
Normal file
@ -0,0 +1,724 @@
|
|||||||
|
// Copyright 2012 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 typechecking of statements.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
// assigment reports whether x can be assigned to a variable of type 'to',
|
||||||
|
// if necessary by attempting to convert untyped values to the appropriate
|
||||||
|
// type. If x.mode == invalid upon return, then assignment has already
|
||||||
|
// issued an error message and the caller doesn't have to report another.
|
||||||
|
// TODO(gri) This latter behavior is for historic reasons and complicates
|
||||||
|
// callers. Needs to be cleaned up.
|
||||||
|
func (check *checker) assignment(x *operand, to Type) bool {
|
||||||
|
if x.mode == invalid {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := x.typ.(*Result); ok {
|
||||||
|
// TODO(gri) elsewhere we use "assignment count mismatch" (consolidate)
|
||||||
|
check.errorf(x.pos(), "%d-valued expression %s used as single value", len(t.Values), x)
|
||||||
|
x.mode = invalid
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
check.convertUntyped(x, to)
|
||||||
|
|
||||||
|
return x.mode != invalid && x.isAssignable(check.ctxt, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign1to1 typechecks a single assignment of the form lhs = rhs (if rhs != nil), or
|
||||||
|
// lhs = x (if rhs == nil). If decl is set, the lhs expression must be an identifier;
|
||||||
|
// if its type is not set, it is deduced from the type of x or set to Typ[Invalid] in
|
||||||
|
// case of an error.
|
||||||
|
//
|
||||||
|
func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota int) {
|
||||||
|
// Start with rhs so we have an expression type
|
||||||
|
// for declarations with implicit type.
|
||||||
|
if x == nil {
|
||||||
|
x = new(operand)
|
||||||
|
check.expr(x, rhs, nil, iota)
|
||||||
|
// don't exit for declarations - we need the lhs first
|
||||||
|
if x.mode == invalid && !decl {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// x.mode == valid || decl
|
||||||
|
|
||||||
|
// lhs may be an identifier
|
||||||
|
ident, _ := lhs.(*ast.Ident)
|
||||||
|
|
||||||
|
// regular assignment; we know x is valid
|
||||||
|
if !decl {
|
||||||
|
// anything can be assigned to the blank identifier
|
||||||
|
if ident != nil && ident.Name == "_" {
|
||||||
|
// the rhs has its final type
|
||||||
|
check.updateExprType(rhs, x.typ, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var z operand
|
||||||
|
check.expr(&z, lhs, nil, -1)
|
||||||
|
if z.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) verify that all other z.mode values
|
||||||
|
// that may appear here are legal
|
||||||
|
if z.mode == constant || !check.assignment(x, z.typ) {
|
||||||
|
if x.mode != invalid {
|
||||||
|
check.errorf(x.pos(), "cannot assign %s to %s", x, &z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// declaration with initialization; lhs must be an identifier
|
||||||
|
if ident == nil {
|
||||||
|
check.errorf(lhs.Pos(), "cannot declare %s", lhs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine typ of lhs: If the object doesn't have a type
|
||||||
|
// yet, determine it from the type of x; if x is invalid,
|
||||||
|
// set the object type to Typ[Invalid].
|
||||||
|
var typ Type
|
||||||
|
obj := check.lookup(ident)
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
|
||||||
|
case nil:
|
||||||
|
// TODO(gri) is this really unreachable?
|
||||||
|
unreachable()
|
||||||
|
|
||||||
|
case *Const:
|
||||||
|
typ = obj.Type // may already be Typ[Invalid]
|
||||||
|
if typ == nil {
|
||||||
|
typ = Typ[Invalid]
|
||||||
|
if x.mode != invalid {
|
||||||
|
typ = x.typ
|
||||||
|
}
|
||||||
|
obj.Type = typ
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Var:
|
||||||
|
typ = obj.Type // may already be Typ[Invalid]
|
||||||
|
if typ == nil {
|
||||||
|
typ = Typ[Invalid]
|
||||||
|
if x.mode != invalid {
|
||||||
|
typ = x.typ
|
||||||
|
if isUntyped(typ) {
|
||||||
|
// convert untyped types to default types
|
||||||
|
if typ == Typ[UntypedNil] {
|
||||||
|
check.errorf(x.pos(), "use of untyped nil")
|
||||||
|
typ = Typ[Invalid]
|
||||||
|
} else {
|
||||||
|
typ = defaultType(typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj.Type = typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing else to check if we don't have a valid lhs or rhs
|
||||||
|
if typ == Typ[Invalid] || x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !check.assignment(x, typ) {
|
||||||
|
if x.mode != invalid {
|
||||||
|
if x.typ != Typ[Invalid] && typ != Typ[Invalid] {
|
||||||
|
check.errorf(x.pos(), "cannot initialize %s (type %s) with %s", ident.Name, typ, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// for constants, set their value
|
||||||
|
if obj, _ := obj.(*Const); obj != nil {
|
||||||
|
obj.Val = exact.MakeUnknown() // failure case: we don't know the constant value
|
||||||
|
if x.mode == constant {
|
||||||
|
if isConstType(x.typ) {
|
||||||
|
obj.Val = x.val
|
||||||
|
} else if x.typ != Typ[Invalid] {
|
||||||
|
check.errorf(x.pos(), "%s has invalid constant type", x)
|
||||||
|
}
|
||||||
|
} else if x.mode != invalid {
|
||||||
|
check.errorf(x.pos(), "%s is not constant", x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignNtoM typechecks a general assignment. If decl is set, the lhs expressions
|
||||||
|
// must be identifiers; if their types are not set, they are deduced from the types
|
||||||
|
// of the corresponding rhs expressions, or set to Typ[Invalid] in case of an error.
|
||||||
|
// Precondition: len(lhs) > 0 .
|
||||||
|
//
|
||||||
|
func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
|
||||||
|
assert(len(lhs) > 0)
|
||||||
|
|
||||||
|
// If the lhs and rhs have corresponding expressions, treat each
|
||||||
|
// matching pair as an individual pair.
|
||||||
|
if len(lhs) == len(rhs) {
|
||||||
|
for i, e := range rhs {
|
||||||
|
check.assign1to1(lhs[i], e, nil, decl, iota)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, the rhs must be a single expression (possibly
|
||||||
|
// a function call returning multiple values, or a comma-ok
|
||||||
|
// expression).
|
||||||
|
if len(rhs) == 1 {
|
||||||
|
// len(lhs) > 1
|
||||||
|
// Start with rhs so we have expression types
|
||||||
|
// for declarations with implicit types.
|
||||||
|
var x operand
|
||||||
|
check.expr(&x, rhs[0], nil, iota)
|
||||||
|
if x.mode == invalid {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, _ := x.typ.(*Result); t != nil && len(lhs) == len(t.Values) {
|
||||||
|
// function result
|
||||||
|
x.mode = value
|
||||||
|
for i, obj := range t.Values {
|
||||||
|
x.expr = nil // TODO(gri) should do better here
|
||||||
|
x.typ = obj.Type
|
||||||
|
check.assign1to1(lhs[i], nil, &x, decl, iota)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.mode == valueok && len(lhs) == 2 {
|
||||||
|
// comma-ok expression
|
||||||
|
x.mode = value
|
||||||
|
check.assign1to1(lhs[0], nil, &x, decl, iota)
|
||||||
|
|
||||||
|
x.typ = Typ[UntypedBool]
|
||||||
|
check.assign1to1(lhs[1], nil, &x, decl, iota)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check.errorf(lhs[0].Pos(), "assignment count mismatch: %d = %d", len(lhs), len(rhs))
|
||||||
|
|
||||||
|
Error:
|
||||||
|
// In case of a declaration, set all lhs types to Typ[Invalid].
|
||||||
|
if decl {
|
||||||
|
for _, e := range lhs {
|
||||||
|
ident, _ := e.(*ast.Ident)
|
||||||
|
if ident == nil {
|
||||||
|
check.errorf(e.Pos(), "cannot declare %s", e)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch obj := check.lookup(ident).(type) {
|
||||||
|
case *Const:
|
||||||
|
obj.Type = Typ[Invalid]
|
||||||
|
case *Var:
|
||||||
|
obj.Type = Typ[Invalid]
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) optionalStmt(s ast.Stmt) {
|
||||||
|
if s != nil {
|
||||||
|
check.stmt(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) stmtList(list []ast.Stmt) {
|
||||||
|
for _, s := range list {
|
||||||
|
check.stmt(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) call(call *ast.CallExpr) {
|
||||||
|
var x operand
|
||||||
|
check.rawExpr(&x, call, nil, -1, false) // don't check if value is used
|
||||||
|
// TODO(gri) If a builtin is called, the builtin must be valid in statement context.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) multipleDefaults(list []ast.Stmt) {
|
||||||
|
var first ast.Stmt
|
||||||
|
for _, s := range list {
|
||||||
|
var d ast.Stmt
|
||||||
|
switch c := s.(type) {
|
||||||
|
case *ast.CaseClause:
|
||||||
|
if len(c.List) == 0 {
|
||||||
|
d = s
|
||||||
|
}
|
||||||
|
case *ast.CommClause:
|
||||||
|
if c.Comm == nil {
|
||||||
|
d = s
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
check.invalidAST(s.Pos(), "case/communication clause expected")
|
||||||
|
}
|
||||||
|
if d != nil {
|
||||||
|
if first != nil {
|
||||||
|
check.errorf(d.Pos(), "multiple defaults (first at %s)", first.Pos())
|
||||||
|
} else {
|
||||||
|
first = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stmt typechecks statement s.
|
||||||
|
func (check *checker) stmt(s ast.Stmt) {
|
||||||
|
switch s := s.(type) {
|
||||||
|
case *ast.BadStmt, *ast.EmptyStmt:
|
||||||
|
// ignore
|
||||||
|
|
||||||
|
case *ast.DeclStmt:
|
||||||
|
d, _ := s.Decl.(*ast.GenDecl)
|
||||||
|
if d == nil || (d.Tok != token.CONST && d.Tok != token.TYPE && d.Tok != token.VAR) {
|
||||||
|
check.invalidAST(token.NoPos, "const, type, or var declaration expected")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if d.Tok == token.CONST {
|
||||||
|
check.assocInitvals(d)
|
||||||
|
}
|
||||||
|
check.decl(d)
|
||||||
|
|
||||||
|
case *ast.LabeledStmt:
|
||||||
|
// TODO(gri) anything to do with label itself?
|
||||||
|
check.stmt(s.Stmt)
|
||||||
|
|
||||||
|
case *ast.ExprStmt:
|
||||||
|
var x operand
|
||||||
|
used := false
|
||||||
|
switch e := unparen(s.X).(type) {
|
||||||
|
case *ast.CallExpr:
|
||||||
|
// function calls are permitted
|
||||||
|
used = true
|
||||||
|
// but some builtins are excluded
|
||||||
|
// (Caution: This evaluates e.Fun twice, once here and once
|
||||||
|
// below as part of s.X. This has consequences for
|
||||||
|
// check.register. Perhaps this can be avoided.)
|
||||||
|
check.expr(&x, e.Fun, nil, -1)
|
||||||
|
if x.mode != invalid {
|
||||||
|
if b, ok := x.typ.(*builtin); ok && !b.isStatement {
|
||||||
|
used = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ast.UnaryExpr:
|
||||||
|
// receive operations are permitted
|
||||||
|
if e.Op == token.ARROW {
|
||||||
|
used = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !used {
|
||||||
|
check.errorf(s.Pos(), "%s not used", s.X)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
check.rawExpr(&x, s.X, nil, -1, false)
|
||||||
|
if x.mode == typexpr {
|
||||||
|
check.errorf(x.pos(), "%s is not an expression", &x)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.SendStmt:
|
||||||
|
var ch, x operand
|
||||||
|
check.expr(&ch, s.Chan, nil, -1)
|
||||||
|
check.expr(&x, s.Value, nil, -1)
|
||||||
|
if ch.mode == invalid || x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tch, ok := underlying(ch.typ).(*Chan); !ok || tch.Dir&ast.SEND == 0 || !check.assignment(&x, tch.Elt) {
|
||||||
|
if x.mode != invalid {
|
||||||
|
check.invalidOp(ch.pos(), "cannot send %s to channel %s", &x, &ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.IncDecStmt:
|
||||||
|
var op token.Token
|
||||||
|
switch s.Tok {
|
||||||
|
case token.INC:
|
||||||
|
op = token.ADD
|
||||||
|
case token.DEC:
|
||||||
|
op = token.SUB
|
||||||
|
default:
|
||||||
|
check.invalidAST(s.TokPos, "unknown inc/dec operation %s", s.Tok)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var x operand
|
||||||
|
Y := &ast.BasicLit{ValuePos: s.X.Pos(), Kind: token.INT, Value: "1"} // use x's position
|
||||||
|
check.binary(&x, s.X, Y, op, -1)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check.assign1to1(s.X, nil, &x, false, -1)
|
||||||
|
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
switch s.Tok {
|
||||||
|
case token.ASSIGN, token.DEFINE:
|
||||||
|
if len(s.Lhs) == 0 {
|
||||||
|
check.invalidAST(s.Pos(), "missing lhs in assignment")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check.assignNtoM(s.Lhs, s.Rhs, s.Tok == token.DEFINE, -1)
|
||||||
|
default:
|
||||||
|
// assignment operations
|
||||||
|
if len(s.Lhs) != 1 || len(s.Rhs) != 1 {
|
||||||
|
check.errorf(s.TokPos, "assignment operation %s requires single-valued expressions", s.Tok)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO(gri) make this conversion more efficient
|
||||||
|
var op token.Token
|
||||||
|
switch s.Tok {
|
||||||
|
case token.ADD_ASSIGN:
|
||||||
|
op = token.ADD
|
||||||
|
case token.SUB_ASSIGN:
|
||||||
|
op = token.SUB
|
||||||
|
case token.MUL_ASSIGN:
|
||||||
|
op = token.MUL
|
||||||
|
case token.QUO_ASSIGN:
|
||||||
|
op = token.QUO
|
||||||
|
case token.REM_ASSIGN:
|
||||||
|
op = token.REM
|
||||||
|
case token.AND_ASSIGN:
|
||||||
|
op = token.AND
|
||||||
|
case token.OR_ASSIGN:
|
||||||
|
op = token.OR
|
||||||
|
case token.XOR_ASSIGN:
|
||||||
|
op = token.XOR
|
||||||
|
case token.SHL_ASSIGN:
|
||||||
|
op = token.SHL
|
||||||
|
case token.SHR_ASSIGN:
|
||||||
|
op = token.SHR
|
||||||
|
case token.AND_NOT_ASSIGN:
|
||||||
|
op = token.AND_NOT
|
||||||
|
default:
|
||||||
|
check.invalidAST(s.TokPos, "unknown assignment operation %s", s.Tok)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var x operand
|
||||||
|
check.binary(&x, s.Lhs[0], s.Rhs[0], op, -1)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check.assign1to1(s.Lhs[0], nil, &x, false, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.GoStmt:
|
||||||
|
check.call(s.Call)
|
||||||
|
|
||||||
|
case *ast.DeferStmt:
|
||||||
|
check.call(s.Call)
|
||||||
|
|
||||||
|
case *ast.ReturnStmt:
|
||||||
|
sig := check.funcsig
|
||||||
|
if n := len(sig.Results); n > 0 {
|
||||||
|
// TODO(gri) should not have to compute lhs, named every single time - clean this up
|
||||||
|
lhs := make([]ast.Expr, n)
|
||||||
|
named := false // if set, function has named results
|
||||||
|
for i, res := range sig.Results {
|
||||||
|
if len(res.Name) > 0 {
|
||||||
|
// a blank (_) result parameter is a named result
|
||||||
|
named = true
|
||||||
|
}
|
||||||
|
name := ast.NewIdent(res.Name)
|
||||||
|
name.NamePos = s.Pos()
|
||||||
|
check.register(name, &Var{Name: res.Name, Type: res.Type}) // Pkg == nil
|
||||||
|
lhs[i] = name
|
||||||
|
}
|
||||||
|
if len(s.Results) > 0 || !named {
|
||||||
|
// TODO(gri) assignNtoM should perhaps not require len(lhs) > 0
|
||||||
|
check.assignNtoM(lhs, s.Results, false, -1)
|
||||||
|
}
|
||||||
|
} else if len(s.Results) > 0 {
|
||||||
|
check.errorf(s.Pos(), "no result values expected")
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.BranchStmt:
|
||||||
|
// TODO(gri) implement this
|
||||||
|
|
||||||
|
case *ast.BlockStmt:
|
||||||
|
check.stmtList(s.List)
|
||||||
|
|
||||||
|
case *ast.IfStmt:
|
||||||
|
check.optionalStmt(s.Init)
|
||||||
|
var x operand
|
||||||
|
check.expr(&x, s.Cond, nil, -1)
|
||||||
|
if x.mode != invalid && !isBoolean(x.typ) {
|
||||||
|
check.errorf(s.Cond.Pos(), "non-boolean condition in if statement")
|
||||||
|
}
|
||||||
|
check.stmt(s.Body)
|
||||||
|
check.optionalStmt(s.Else)
|
||||||
|
|
||||||
|
case *ast.SwitchStmt:
|
||||||
|
check.optionalStmt(s.Init)
|
||||||
|
var x operand
|
||||||
|
tag := s.Tag
|
||||||
|
if tag == nil {
|
||||||
|
// use fake true tag value and position it at the opening { of the switch
|
||||||
|
ident := &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"}
|
||||||
|
check.register(ident, Universe.Lookup("true"))
|
||||||
|
tag = ident
|
||||||
|
}
|
||||||
|
check.expr(&x, tag, nil, -1)
|
||||||
|
|
||||||
|
check.multipleDefaults(s.Body.List)
|
||||||
|
// TODO(gri) check also correct use of fallthrough
|
||||||
|
seen := make(map[interface{}]token.Pos)
|
||||||
|
for _, s := range s.Body.List {
|
||||||
|
clause, _ := s.(*ast.CaseClause)
|
||||||
|
if clause == nil {
|
||||||
|
continue // error reported before
|
||||||
|
}
|
||||||
|
if x.mode != invalid {
|
||||||
|
for _, expr := range clause.List {
|
||||||
|
x := x // copy of x (don't modify original)
|
||||||
|
var y operand
|
||||||
|
check.expr(&y, expr, nil, -1)
|
||||||
|
if y.mode == invalid {
|
||||||
|
continue // error reported before
|
||||||
|
}
|
||||||
|
// If we have a constant case value, it must appear only
|
||||||
|
// once in the switch statement. Determine if there is a
|
||||||
|
// duplicate entry, but only report an error if there are
|
||||||
|
// no other errors.
|
||||||
|
var dupl token.Pos
|
||||||
|
var yy operand
|
||||||
|
if y.mode == constant {
|
||||||
|
// TODO(gri) This code doesn't work correctly for
|
||||||
|
// large integer, floating point, or
|
||||||
|
// complex values - the respective struct
|
||||||
|
// comparisons are shallow. Need to use a
|
||||||
|
// hash function to index the map.
|
||||||
|
dupl = seen[y.val]
|
||||||
|
seen[y.val] = y.pos()
|
||||||
|
yy = y // remember y
|
||||||
|
}
|
||||||
|
// TODO(gri) The convertUntyped call pair below appears in other places. Factor!
|
||||||
|
// Order matters: By comparing y against x, error positions are at the case values.
|
||||||
|
check.convertUntyped(&y, x.typ)
|
||||||
|
if y.mode == invalid {
|
||||||
|
continue // error reported before
|
||||||
|
}
|
||||||
|
check.convertUntyped(&x, y.typ)
|
||||||
|
if x.mode == invalid {
|
||||||
|
continue // error reported before
|
||||||
|
}
|
||||||
|
check.comparison(&y, &x, token.EQL)
|
||||||
|
if y.mode != invalid && dupl.IsValid() {
|
||||||
|
check.errorf(yy.pos(), "%s is duplicate case (previous at %s)",
|
||||||
|
&yy, check.fset.Position(dupl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check.stmtList(clause.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.TypeSwitchStmt:
|
||||||
|
check.optionalStmt(s.Init)
|
||||||
|
|
||||||
|
// A type switch guard must be of the form:
|
||||||
|
//
|
||||||
|
// TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
|
||||||
|
//
|
||||||
|
// The parser is checking syntactic correctness;
|
||||||
|
// remaining syntactic errors are considered AST errors here.
|
||||||
|
// TODO(gri) better factoring of error handling (invalid ASTs)
|
||||||
|
//
|
||||||
|
var lhs *Var // lhs variable or nil
|
||||||
|
var rhs ast.Expr
|
||||||
|
switch guard := s.Assign.(type) {
|
||||||
|
case *ast.ExprStmt:
|
||||||
|
rhs = guard.X
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 {
|
||||||
|
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ident, _ := guard.Lhs[0].(*ast.Ident)
|
||||||
|
if ident == nil {
|
||||||
|
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lhs = check.lookup(ident).(*Var)
|
||||||
|
rhs = guard.Rhs[0]
|
||||||
|
default:
|
||||||
|
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// rhs must be of the form: expr.(type) and expr must be an interface
|
||||||
|
expr, _ := rhs.(*ast.TypeAssertExpr)
|
||||||
|
if expr == nil || expr.Type != nil {
|
||||||
|
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var x operand
|
||||||
|
check.expr(&x, expr.X, nil, -1)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var T *Interface
|
||||||
|
if T, _ = underlying(x.typ).(*Interface); T == nil {
|
||||||
|
check.errorf(x.pos(), "%s is not an interface", &x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
check.multipleDefaults(s.Body.List)
|
||||||
|
for _, s := range s.Body.List {
|
||||||
|
clause, _ := s.(*ast.CaseClause)
|
||||||
|
if clause == nil {
|
||||||
|
continue // error reported before
|
||||||
|
}
|
||||||
|
// Check each type in this type switch case.
|
||||||
|
var typ Type
|
||||||
|
for _, expr := range clause.List {
|
||||||
|
typ = check.typOrNil(expr, false)
|
||||||
|
if typ != nil && typ != Typ[Invalid] {
|
||||||
|
if method, wrongType := missingMethod(typ, T); method != nil {
|
||||||
|
var msg string
|
||||||
|
if wrongType {
|
||||||
|
msg = "%s cannot have dynamic type %s (wrong type for method %s)"
|
||||||
|
} else {
|
||||||
|
msg = "%s cannot have dynamic type %s (missing method %s)"
|
||||||
|
}
|
||||||
|
check.errorf(expr.Pos(), msg, &x, typ, method.Name)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If lhs exists, set its type for each clause.
|
||||||
|
if lhs != nil {
|
||||||
|
// In clauses with a case listing exactly one type, the variable has that type;
|
||||||
|
// otherwise, the variable has the type of the expression in the TypeSwitchGuard.
|
||||||
|
if len(clause.List) != 1 || typ == nil {
|
||||||
|
typ = x.typ
|
||||||
|
}
|
||||||
|
lhs.Type = typ
|
||||||
|
}
|
||||||
|
check.stmtList(clause.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is only one object (lhs) associated with a lhs identifier, but that object
|
||||||
|
// assumes different types for different clauses. Set it back to the type of the
|
||||||
|
// TypeSwitchGuard expression so that that variable always has a valid type.
|
||||||
|
if lhs != nil {
|
||||||
|
lhs.Type = x.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.SelectStmt:
|
||||||
|
check.multipleDefaults(s.Body.List)
|
||||||
|
for _, s := range s.Body.List {
|
||||||
|
clause, _ := s.(*ast.CommClause)
|
||||||
|
if clause == nil {
|
||||||
|
continue // error reported before
|
||||||
|
}
|
||||||
|
check.optionalStmt(clause.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt)
|
||||||
|
check.stmtList(clause.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ForStmt:
|
||||||
|
check.optionalStmt(s.Init)
|
||||||
|
if s.Cond != nil {
|
||||||
|
var x operand
|
||||||
|
check.expr(&x, s.Cond, nil, -1)
|
||||||
|
if x.mode != invalid && !isBoolean(x.typ) {
|
||||||
|
check.errorf(s.Cond.Pos(), "non-boolean condition in for statement")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check.optionalStmt(s.Post)
|
||||||
|
check.stmt(s.Body)
|
||||||
|
|
||||||
|
case *ast.RangeStmt:
|
||||||
|
// check expression to iterate over
|
||||||
|
decl := s.Tok == token.DEFINE
|
||||||
|
var x operand
|
||||||
|
check.expr(&x, s.X, nil, -1)
|
||||||
|
if x.mode == invalid {
|
||||||
|
// if we don't have a declaration, we can still check the loop's body
|
||||||
|
if !decl {
|
||||||
|
check.stmt(s.Body)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine key/value types
|
||||||
|
var key, val Type
|
||||||
|
switch typ := underlying(x.typ).(type) {
|
||||||
|
case *Basic:
|
||||||
|
if isString(typ) {
|
||||||
|
key = Typ[UntypedInt]
|
||||||
|
val = Typ[UntypedRune]
|
||||||
|
}
|
||||||
|
case *Array:
|
||||||
|
key = Typ[UntypedInt]
|
||||||
|
val = typ.Elt
|
||||||
|
case *Slice:
|
||||||
|
key = Typ[UntypedInt]
|
||||||
|
val = typ.Elt
|
||||||
|
case *Pointer:
|
||||||
|
if typ, _ := underlying(typ.Base).(*Array); typ != nil {
|
||||||
|
key = Typ[UntypedInt]
|
||||||
|
val = typ.Elt
|
||||||
|
}
|
||||||
|
case *Map:
|
||||||
|
key = typ.Key
|
||||||
|
val = typ.Elt
|
||||||
|
case *Chan:
|
||||||
|
key = typ.Elt
|
||||||
|
if typ.Dir&ast.RECV == 0 {
|
||||||
|
check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
if s.Value != nil {
|
||||||
|
check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if key == nil {
|
||||||
|
check.errorf(x.pos(), "cannot range over %s", &x)
|
||||||
|
// if we don't have a declaration, we can still check the loop's body
|
||||||
|
if !decl {
|
||||||
|
check.stmt(s.Body)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check assignment to/declaration of iteration variables
|
||||||
|
// TODO(gri) The error messages/positions are not great here,
|
||||||
|
// they refer to the expression in the range clause.
|
||||||
|
// Should give better messages w/o too much code
|
||||||
|
// duplication (assignment checking).
|
||||||
|
x.mode = value
|
||||||
|
if s.Key != nil {
|
||||||
|
x.typ = key
|
||||||
|
x.expr = s.Key
|
||||||
|
check.assign1to1(s.Key, nil, &x, decl, -1)
|
||||||
|
} else {
|
||||||
|
check.invalidAST(s.Pos(), "range clause requires index iteration variable")
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
if s.Value != nil {
|
||||||
|
x.typ = val
|
||||||
|
x.expr = s.Value
|
||||||
|
check.assign1to1(s.Value, nil, &x, decl, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
check.stmt(s.Body)
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.errorf(s.Pos(), "invalid statement")
|
||||||
|
}
|
||||||
|
}
|
432
go/types/testdata/builtins.src
vendored
Normal file
432
go/types/testdata/builtins.src
vendored
Normal file
@ -0,0 +1,432 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// builtin calls
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
func _append() {
|
||||||
|
var x int
|
||||||
|
var s []byte
|
||||||
|
_0 := append /* ERROR "argument" */ ()
|
||||||
|
_1 := append("foo" /* ERROR "not a typed slice" */)
|
||||||
|
_2 := append(nil /* ERROR "not a typed slice" */, s)
|
||||||
|
_3 := append(x /* ERROR "not a typed slice" */, s)
|
||||||
|
_4 := append(s)
|
||||||
|
append /* ERROR "not used" */ (s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _cap() {
|
||||||
|
var a [10]bool
|
||||||
|
var p *[20]int
|
||||||
|
var s []int
|
||||||
|
var c chan string
|
||||||
|
_0 := cap /* ERROR "argument" */ ()
|
||||||
|
_1 := cap /* ERROR "argument" */ (1, 2)
|
||||||
|
_2 := cap(42 /* ERROR "invalid" */)
|
||||||
|
const _3 = cap(a)
|
||||||
|
assert(_3 == 10)
|
||||||
|
const _4 = cap(p)
|
||||||
|
assert(_4 == 20)
|
||||||
|
_5 := cap(c)
|
||||||
|
cap /* ERROR "not used" */ (c)
|
||||||
|
|
||||||
|
// issue 4744
|
||||||
|
type T struct{ a [10]int }
|
||||||
|
const _ = cap(((*T)(nil)).a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _close() {
|
||||||
|
var c chan int
|
||||||
|
var r <-chan int
|
||||||
|
close /* ERROR "argument" */ ()
|
||||||
|
close /* ERROR "argument" */ (1, 2)
|
||||||
|
close(42 /* ERROR "not a channel" */)
|
||||||
|
close(r /* ERROR "receive-only channel" */)
|
||||||
|
close(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _complex() {
|
||||||
|
var i32 int32
|
||||||
|
var f32 float32
|
||||||
|
var f64 float64
|
||||||
|
var c64 complex64
|
||||||
|
var c128 complex128
|
||||||
|
_ = complex /* ERROR "argument" */ ()
|
||||||
|
_ = complex /* ERROR "argument" */ (1)
|
||||||
|
_ = complex(true /* ERROR "invalid argument" */ , 0)
|
||||||
|
_ = complex(i32 /* ERROR "invalid argument" */ , 0)
|
||||||
|
_ = complex("foo" /* ERROR "invalid argument" */ , 0)
|
||||||
|
_ = complex(c64 /* ERROR "invalid argument" */ , 0)
|
||||||
|
_ = complex(0, true /* ERROR "invalid argument" */ )
|
||||||
|
_ = complex(0, i32 /* ERROR "invalid argument" */ )
|
||||||
|
_ = complex(0, "foo" /* ERROR "invalid argument" */ )
|
||||||
|
_ = complex(0, c64 /* ERROR "invalid argument" */ )
|
||||||
|
_ = complex(f32, f32)
|
||||||
|
_ = complex(f32, 1)
|
||||||
|
_ = complex(f32, 1.0)
|
||||||
|
_ = complex(f32, 'a')
|
||||||
|
_ = complex(f64, f64)
|
||||||
|
_ = complex(f64, 1)
|
||||||
|
_ = complex(f64, 1.0)
|
||||||
|
_ = complex(f64, 'a')
|
||||||
|
_ = complex(f32 /* ERROR "mismatched types" */, f64)
|
||||||
|
_ = complex(f64 /* ERROR "mismatched types" */, f32)
|
||||||
|
_ = complex(1, 1)
|
||||||
|
_ = complex(1, 1.1)
|
||||||
|
_ = complex(1, 'a')
|
||||||
|
complex /* ERROR "not used" */ (1, 2)
|
||||||
|
|
||||||
|
var _ complex64 = complex(f32, f32)
|
||||||
|
var _ complex64 = complex /* ERROR "cannot initialize" */ (f64, f64)
|
||||||
|
|
||||||
|
var _ complex128 = complex /* ERROR "cannot initialize" */ (f32, f32)
|
||||||
|
var _ complex128 = complex(f64, f64)
|
||||||
|
|
||||||
|
// untyped constants
|
||||||
|
const _ int = complex(1, 0)
|
||||||
|
const _ float32 = complex(1, 0)
|
||||||
|
const _ complex64 = complex(1, 0)
|
||||||
|
const _ complex128 = complex(1, 0)
|
||||||
|
|
||||||
|
const _ int = complex /* ERROR "int" */ (1.1, 0)
|
||||||
|
const _ float32 = complex /* ERROR "float32" */ (1, 2)
|
||||||
|
|
||||||
|
// untyped values
|
||||||
|
var s uint
|
||||||
|
_ = complex(1 /* ERROR "integer" */ <<s, 0)
|
||||||
|
const _ = complex /* ERROR "not constant" */ (1 /* ERROR "integer" */ <<s, 0)
|
||||||
|
var _ int = complex /* ERROR "cannot initialize" */ (1 /* ERROR "integer" */ <<s, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _copy() {
|
||||||
|
copy /* ERROR "not enough arguments" */ ()
|
||||||
|
copy /* ERROR "not enough arguments" */ ("foo")
|
||||||
|
copy([ /* ERROR "copy expects slice arguments" */ ...]int{}, []int{})
|
||||||
|
copy([ /* ERROR "copy expects slice arguments" */ ]int{}, [...]int{})
|
||||||
|
copy([ /* ERROR "different element types" */ ]int8{}, "foo")
|
||||||
|
|
||||||
|
// spec examples
|
||||||
|
var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
|
||||||
|
var s = make([]int, 6)
|
||||||
|
var b = make([]byte, 5)
|
||||||
|
n1 := copy(s, a[0:]) // n1 == 6, s == []int{0, 1, 2, 3, 4, 5}
|
||||||
|
n2 := copy(s, s[2:]) // n2 == 4, s == []int{2, 3, 4, 5, 4, 5}
|
||||||
|
n3 := copy(b, "Hello, World!") // n3 == 5, b == []byte("Hello")
|
||||||
|
}
|
||||||
|
|
||||||
|
func _delete() {
|
||||||
|
var m map[string]int
|
||||||
|
var s string
|
||||||
|
delete /* ERROR "argument" */ ()
|
||||||
|
delete /* ERROR "argument" */ (1)
|
||||||
|
delete /* ERROR "argument" */ (1, 2, 3)
|
||||||
|
delete(m, 0 /* ERROR "not assignable" */)
|
||||||
|
delete(m, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _imag() {
|
||||||
|
var f32 float32
|
||||||
|
var f64 float64
|
||||||
|
var c64 complex64
|
||||||
|
var c128 complex128
|
||||||
|
_ = imag /* ERROR "argument" */ ()
|
||||||
|
_ = imag /* ERROR "argument" */ (1, 2)
|
||||||
|
_ = imag(10 /* ERROR "must be a complex number" */)
|
||||||
|
_ = imag(2.7182818 /* ERROR "must be a complex number" */)
|
||||||
|
_ = imag("foo" /* ERROR "must be a complex number" */)
|
||||||
|
const _5 = imag(1 + 2i)
|
||||||
|
assert(_5 == 2)
|
||||||
|
f32 = _5
|
||||||
|
f64 = _5
|
||||||
|
const _6 = imag(0i)
|
||||||
|
assert(_6 == 0)
|
||||||
|
f32 = imag(c64)
|
||||||
|
f64 = imag(c128)
|
||||||
|
f32 = imag /* ERROR "cannot assign" */ (c128)
|
||||||
|
f64 = imag /* ERROR "cannot assign" */ (c64)
|
||||||
|
imag /* ERROR "not used" */ (c64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _len() {
|
||||||
|
const c = "foobar"
|
||||||
|
var a [10]bool
|
||||||
|
var p *[20]int
|
||||||
|
var s []int
|
||||||
|
var m map[string]complex128
|
||||||
|
_ = len /* ERROR "argument" */ ()
|
||||||
|
_ = len /* ERROR "argument" */ (1, 2)
|
||||||
|
_ = len(42 /* ERROR "invalid" */)
|
||||||
|
const _3 = len(c)
|
||||||
|
assert(_3 == 6)
|
||||||
|
const _4 = len(a)
|
||||||
|
assert(_4 == 10)
|
||||||
|
const _5 = len(p)
|
||||||
|
assert(_5 == 20)
|
||||||
|
_ = len(m)
|
||||||
|
len /* ERROR "not used" */ (c)
|
||||||
|
|
||||||
|
// esoteric case
|
||||||
|
var t string
|
||||||
|
var hash map[interface{}][]*[10]int
|
||||||
|
const n = len /* ERROR "not constant" */ (hash[recover()][len(t)])
|
||||||
|
assert(n == 10) // ok because n has unknown value and no error is reported
|
||||||
|
var ch <-chan int
|
||||||
|
const nn = len /* ERROR "not constant" */ (hash[<-ch][len(t)])
|
||||||
|
|
||||||
|
// issue 4744
|
||||||
|
type T struct{ a [10]int }
|
||||||
|
const _ = len(((*T)(nil)).a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _make() {
|
||||||
|
var n int
|
||||||
|
var m float32
|
||||||
|
var s uint
|
||||||
|
|
||||||
|
_ = make /* ERROR "argument" */ ()
|
||||||
|
_ = make(1 /* ERROR "not a type" */)
|
||||||
|
_ = make(int /* ERROR "cannot make" */)
|
||||||
|
|
||||||
|
// slices
|
||||||
|
_ = make/* ERROR "arguments" */ ([]int)
|
||||||
|
_ = make/* ERROR "arguments" */ ([]int, 2, 3, 4)
|
||||||
|
_ = make([]int, int /* ERROR "not an expression" */)
|
||||||
|
_ = make([]int, 10, float32 /* ERROR "not an expression" */)
|
||||||
|
_ = make([]int, "foo" /* ERROR "cannot convert" */)
|
||||||
|
_ = make([]int, 10, 2.3 /* ERROR "overflows" */)
|
||||||
|
_ = make([]int, 5, 10.0)
|
||||||
|
_ = make([]int, 0i)
|
||||||
|
_ = make([]int, 1.0)
|
||||||
|
_ = make([]int, 1.0<<s)
|
||||||
|
_ = make([]int, 1.1 /* ERROR "int" */ <<s)
|
||||||
|
_ = make([]int, - /* ERROR "must not be negative" */ 1, 10)
|
||||||
|
_ = make([]int, 0, - /* ERROR "must not be negative" */ 1)
|
||||||
|
_ = make([]int, - /* ERROR "must not be negative" */ 1, - /* ERROR "must not be negative" */ 1)
|
||||||
|
_ = make([]int, 1 /* ERROR "overflows" */ <<100, 1 /* ERROR "overflows" */ <<100)
|
||||||
|
_ = make([]int, 10 /* ERROR "length and capacity swapped" */ , 9)
|
||||||
|
_ = make([]int, 1 /* ERROR "overflows" */ <<100, 12345)
|
||||||
|
_ = make([]int, m /* ERROR "must be integer" */ )
|
||||||
|
|
||||||
|
// maps
|
||||||
|
_ = make /* ERROR "arguments" */ (map[int]string, 10, 20)
|
||||||
|
_ = make(map[int]float32, int /* ERROR "not an expression" */)
|
||||||
|
_ = make(map[int]float32, "foo" /* ERROR "cannot convert" */)
|
||||||
|
_ = make(map[int]float32, 10)
|
||||||
|
_ = make(map[int]float32, n)
|
||||||
|
_ = make(map[int]float32, int64(n))
|
||||||
|
_ = make(map[string]bool, 10.0)
|
||||||
|
_ = make(map[string]bool, 10.0<<s)
|
||||||
|
|
||||||
|
// channels
|
||||||
|
_ = make /* ERROR "arguments" */ (chan int, 10, 20)
|
||||||
|
_ = make(chan int, int /* ERROR "not an expression" */)
|
||||||
|
_ = make(chan<- int, "foo" /* ERROR "cannot convert" */)
|
||||||
|
_ = make(<-chan float64, 10)
|
||||||
|
_ = make(chan chan int, n)
|
||||||
|
_ = make(chan string, int64(n))
|
||||||
|
_ = make(chan bool, 10.0)
|
||||||
|
_ = make(chan bool, 10.0<<s)
|
||||||
|
|
||||||
|
make /* ERROR "not used" */ ([]int, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _new() {
|
||||||
|
_ = new /* ERROR "argument" */ ()
|
||||||
|
_ = new /* ERROR "argument" */ (1, 2)
|
||||||
|
_ = new("foo" /* ERROR "not a type" */)
|
||||||
|
p := new(float64)
|
||||||
|
_ = new(struct{ x, y int })
|
||||||
|
q := new(*float64)
|
||||||
|
_ = *p == **q
|
||||||
|
new /* ERROR "not used" */ (int)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _panic() {
|
||||||
|
panic /* ERROR "arguments" */ ()
|
||||||
|
panic /* ERROR "arguments" */ (1, 2)
|
||||||
|
panic(0)
|
||||||
|
panic("foo")
|
||||||
|
panic(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _print() {
|
||||||
|
print()
|
||||||
|
print(1)
|
||||||
|
print(1, 2)
|
||||||
|
print("foo")
|
||||||
|
print(2.718281828)
|
||||||
|
print(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _println() {
|
||||||
|
println()
|
||||||
|
println(1)
|
||||||
|
println(1, 2)
|
||||||
|
println("foo")
|
||||||
|
println(2.718281828)
|
||||||
|
println(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _real() {
|
||||||
|
var f32 float32
|
||||||
|
var f64 float64
|
||||||
|
var c64 complex64
|
||||||
|
var c128 complex128
|
||||||
|
_ = real /* ERROR "argument" */ ()
|
||||||
|
_ = real /* ERROR "argument" */ (1, 2)
|
||||||
|
_ = real(10 /* ERROR "must be a complex number" */)
|
||||||
|
_ = real(2.7182818 /* ERROR "must be a complex number" */)
|
||||||
|
_ = real("foo" /* ERROR "must be a complex number" */)
|
||||||
|
const _5 = real(1 + 2i)
|
||||||
|
assert(_5 == 1)
|
||||||
|
f32 = _5
|
||||||
|
f64 = _5
|
||||||
|
const _6 = real(0i)
|
||||||
|
assert(_6 == 0)
|
||||||
|
f32 = real(c64)
|
||||||
|
f64 = real(c128)
|
||||||
|
f32 = real /* ERROR "cannot assign" */ (c128)
|
||||||
|
f64 = real /* ERROR "cannot assign" */ (c64)
|
||||||
|
real /* ERROR "not used" */ (c64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _recover() {
|
||||||
|
_ = recover()
|
||||||
|
_ = recover /* ERROR "argument" */ (10)
|
||||||
|
recover()
|
||||||
|
}
|
||||||
|
|
||||||
|
// assuming types.DefaultPtrSize == 8
|
||||||
|
type S0 struct{ // offset
|
||||||
|
a bool // 0
|
||||||
|
b rune // 4
|
||||||
|
c *int // 8
|
||||||
|
d bool // 16
|
||||||
|
e complex128 // 24
|
||||||
|
} // 40
|
||||||
|
|
||||||
|
type S1 struct{ // offset
|
||||||
|
x float32 // 0
|
||||||
|
y string // 8
|
||||||
|
z *S1 // 24
|
||||||
|
S0 // 32
|
||||||
|
} // 72
|
||||||
|
|
||||||
|
type S2 struct{ // offset
|
||||||
|
*S1 // 0
|
||||||
|
} // 8
|
||||||
|
|
||||||
|
func _Alignof() {
|
||||||
|
var x int
|
||||||
|
_ = unsafe /* ERROR "argument" */ .Alignof()
|
||||||
|
_ = unsafe /* ERROR "argument" */ .Alignof(1, 2)
|
||||||
|
_ = unsafe.Alignof(int /* ERROR "not an expression" */)
|
||||||
|
_ = unsafe.Alignof(42)
|
||||||
|
_ = unsafe.Alignof(new(struct{}))
|
||||||
|
unsafe /* ERROR "not used" */ .Alignof(x)
|
||||||
|
|
||||||
|
var y S0
|
||||||
|
assert(unsafe.Alignof(y.a) == 1)
|
||||||
|
assert(unsafe.Alignof(y.b) == 4)
|
||||||
|
assert(unsafe.Alignof(y.c) == 8)
|
||||||
|
assert(unsafe.Alignof(y.d) == 1)
|
||||||
|
assert(unsafe.Alignof(y.e) == 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Offsetof() {
|
||||||
|
var x struct{ f int }
|
||||||
|
_ = unsafe /* ERROR "argument" */ .Offsetof()
|
||||||
|
_ = unsafe /* ERROR "argument" */ .Offsetof(1, 2)
|
||||||
|
_ = unsafe.Offsetof(int /* ERROR "not a selector expression" */)
|
||||||
|
_ = unsafe.Offsetof(x /* ERROR "not a selector expression" */)
|
||||||
|
_ = unsafe.Offsetof(x.f)
|
||||||
|
_ = unsafe.Offsetof((x.f))
|
||||||
|
_ = unsafe.Offsetof((((((((x))).f)))))
|
||||||
|
unsafe /* ERROR "not used" */ .Offsetof(x.f)
|
||||||
|
|
||||||
|
var y0 S0
|
||||||
|
assert(unsafe.Offsetof(y0.a) == 0)
|
||||||
|
assert(unsafe.Offsetof(y0.b) == 4)
|
||||||
|
assert(unsafe.Offsetof(y0.c) == 8)
|
||||||
|
assert(unsafe.Offsetof(y0.d) == 16)
|
||||||
|
assert(unsafe.Offsetof(y0.e) == 24)
|
||||||
|
|
||||||
|
var y1 S1
|
||||||
|
assert(unsafe.Offsetof(y1.x) == 0)
|
||||||
|
assert(unsafe.Offsetof(y1.y) == 8)
|
||||||
|
assert(unsafe.Offsetof(y1.z) == 24)
|
||||||
|
assert(unsafe.Offsetof(y1.S0) == 32)
|
||||||
|
|
||||||
|
assert(unsafe.Offsetof(y1.S0.a) == 0) // relative to S0
|
||||||
|
assert(unsafe.Offsetof(y1.a) == 32) // relative to S1
|
||||||
|
assert(unsafe.Offsetof(y1.b) == 36) // relative to S1
|
||||||
|
assert(unsafe.Offsetof(y1.c) == 40) // relative to S1
|
||||||
|
assert(unsafe.Offsetof(y1.d) == 48) // relative to S1
|
||||||
|
assert(unsafe.Offsetof(y1.e) == 56) // relative to S1
|
||||||
|
|
||||||
|
var y2 S2
|
||||||
|
assert(unsafe.Offsetof(y2.S1) == 0)
|
||||||
|
_ = unsafe.Offsetof(y2 /* ERROR "embedded via a pointer" */ .x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Sizeof() {
|
||||||
|
var x int
|
||||||
|
_ = unsafe /* ERROR "argument" */ .Sizeof()
|
||||||
|
_ = unsafe /* ERROR "argument" */ .Sizeof(1, 2)
|
||||||
|
_ = unsafe.Sizeof(int /* ERROR "not an expression" */)
|
||||||
|
_ = unsafe.Sizeof(42)
|
||||||
|
_ = unsafe.Sizeof(new(complex128))
|
||||||
|
unsafe /* ERROR "not used" */ .Sizeof(x)
|
||||||
|
|
||||||
|
// basic types have size guarantees
|
||||||
|
assert(unsafe.Sizeof(byte(0)) == 1)
|
||||||
|
assert(unsafe.Sizeof(uint8(0)) == 1)
|
||||||
|
assert(unsafe.Sizeof(int8(0)) == 1)
|
||||||
|
assert(unsafe.Sizeof(uint16(0)) == 2)
|
||||||
|
assert(unsafe.Sizeof(int16(0)) == 2)
|
||||||
|
assert(unsafe.Sizeof(uint32(0)) == 4)
|
||||||
|
assert(unsafe.Sizeof(int32(0)) == 4)
|
||||||
|
assert(unsafe.Sizeof(float32(0)) == 4)
|
||||||
|
assert(unsafe.Sizeof(uint64(0)) == 8)
|
||||||
|
assert(unsafe.Sizeof(int64(0)) == 8)
|
||||||
|
assert(unsafe.Sizeof(float64(0)) == 8)
|
||||||
|
assert(unsafe.Sizeof(complex64(0)) == 8)
|
||||||
|
assert(unsafe.Sizeof(complex128(0)) == 16)
|
||||||
|
|
||||||
|
var y0 S0
|
||||||
|
assert(unsafe.Sizeof(y0.a) == 1)
|
||||||
|
assert(unsafe.Sizeof(y0.b) == 4)
|
||||||
|
assert(unsafe.Sizeof(y0.c) == 8)
|
||||||
|
assert(unsafe.Sizeof(y0.d) == 1)
|
||||||
|
assert(unsafe.Sizeof(y0.e) == 16)
|
||||||
|
assert(unsafe.Sizeof(y0) == 40)
|
||||||
|
|
||||||
|
var y1 S1
|
||||||
|
assert(unsafe.Sizeof(y1) == 72)
|
||||||
|
|
||||||
|
var y2 S2
|
||||||
|
assert(unsafe.Sizeof(y2) == 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// self-testing only
|
||||||
|
func _assert() {
|
||||||
|
var x int
|
||||||
|
assert /* ERROR "argument" */ ()
|
||||||
|
assert /* ERROR "argument" */ (1, 2)
|
||||||
|
assert("foo" /* ERROR "boolean constant" */ )
|
||||||
|
assert(x /* ERROR "boolean constant" */)
|
||||||
|
assert(true)
|
||||||
|
assert /* ERROR "failed" */ (false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// self-testing only
|
||||||
|
func _trace() {
|
||||||
|
// Uncomment the code below to test trace - will produce console output
|
||||||
|
// _ = trace /* ERROR "no value" */ ()
|
||||||
|
// _ = trace(1)
|
||||||
|
// _ = trace(true, 1.2, '\'', "foo", 42i, "foo" <= "bar")
|
||||||
|
}
|
215
go/types/testdata/const0.src
vendored
Normal file
215
go/types/testdata/const0.src
vendored
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// constant declarations
|
||||||
|
|
||||||
|
package const0
|
||||||
|
|
||||||
|
// constants declarations must be initialized by constants
|
||||||
|
var x = 0
|
||||||
|
const c0 = x /* ERROR "not constant" */
|
||||||
|
|
||||||
|
// untyped constants
|
||||||
|
const (
|
||||||
|
// boolean values
|
||||||
|
ub0 = false
|
||||||
|
ub1 = true
|
||||||
|
ub2 = 2 < 1
|
||||||
|
ub3 = ui1 == uf1
|
||||||
|
ub4 = true /* ERROR "cannot convert" */ == 0
|
||||||
|
|
||||||
|
// integer values
|
||||||
|
ui0 = 0
|
||||||
|
ui1 = 1
|
||||||
|
ui2 = 42
|
||||||
|
ui3 = 3141592653589793238462643383279502884197169399375105820974944592307816406286
|
||||||
|
ui4 = -10
|
||||||
|
|
||||||
|
ui5 = ui0 + ui1
|
||||||
|
ui6 = ui1 - ui1
|
||||||
|
ui7 = ui2 * ui1
|
||||||
|
ui8 = ui3 / ui3
|
||||||
|
ui9 = ui3 % ui3
|
||||||
|
|
||||||
|
ui10 = 1 / 0 /* ERROR "division by zero" */
|
||||||
|
ui11 = ui1 / 0 /* ERROR "division by zero" */
|
||||||
|
ui12 = ui3 / ui0 /* ERROR "division by zero" */
|
||||||
|
ui13 = 1 % 0 /* ERROR "division by zero" */
|
||||||
|
ui14 = ui1 % 0 /* ERROR "division by zero" */
|
||||||
|
ui15 = ui3 % ui0 /* ERROR "division by zero" */
|
||||||
|
|
||||||
|
ui16 = ui2 & ui3
|
||||||
|
ui17 = ui2 | ui3
|
||||||
|
ui18 = ui2 ^ ui3
|
||||||
|
|
||||||
|
// floating point values
|
||||||
|
uf0 = 0.
|
||||||
|
uf1 = 1.
|
||||||
|
uf2 = 4.2e1
|
||||||
|
uf3 = 3.141592653589793238462643383279502884197169399375105820974944592307816406286
|
||||||
|
uf4 = 1e-1
|
||||||
|
|
||||||
|
uf5 = uf0 + uf1
|
||||||
|
uf6 = uf1 - uf1
|
||||||
|
uf7 = uf2 * uf1
|
||||||
|
uf8 = uf3 / uf3
|
||||||
|
uf9 = uf3 /* ERROR "not defined" */ % uf3
|
||||||
|
|
||||||
|
uf10 = 1 / 0 /* ERROR "division by zero" */
|
||||||
|
uf11 = uf1 / 0 /* ERROR "division by zero" */
|
||||||
|
uf12 = uf3 / uf0 /* ERROR "division by zero" */
|
||||||
|
|
||||||
|
uf16 = uf2 /* ERROR "not defined" */ & uf3
|
||||||
|
uf17 = uf2 /* ERROR "not defined" */ | uf3
|
||||||
|
uf18 = uf2 /* ERROR "not defined" */ ^ uf3
|
||||||
|
|
||||||
|
// complex values
|
||||||
|
uc0 = 0.i
|
||||||
|
uc1 = 1.i
|
||||||
|
uc2 = 4.2e1i
|
||||||
|
uc3 = 3.141592653589793238462643383279502884197169399375105820974944592307816406286i
|
||||||
|
uc4 = 1e-1i
|
||||||
|
|
||||||
|
uc5 = uc0 + uc1
|
||||||
|
uc6 = uc1 - uc1
|
||||||
|
uc7 = uc2 * uc1
|
||||||
|
uc8 = uc3 / uc3
|
||||||
|
uc9 = uc3 /* ERROR "not defined" */ % uc3
|
||||||
|
|
||||||
|
uc10 = 1 / 0 /* ERROR "division by zero" */
|
||||||
|
uc11 = uc1 / 0 /* ERROR "division by zero" */
|
||||||
|
uc12 = uc3 / uc0 /* ERROR "division by zero" */
|
||||||
|
|
||||||
|
uc16 = uc2 /* ERROR "not defined" */ & uc3
|
||||||
|
uc17 = uc2 /* ERROR "not defined" */ | uc3
|
||||||
|
uc18 = uc2 /* ERROR "not defined" */ ^ uc3
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
mybool bool
|
||||||
|
myint int
|
||||||
|
myfloat float64
|
||||||
|
mycomplex complex128
|
||||||
|
)
|
||||||
|
|
||||||
|
// typed constants
|
||||||
|
const (
|
||||||
|
// boolean values
|
||||||
|
tb0 bool = false
|
||||||
|
tb1 bool = true
|
||||||
|
tb2 mybool = 2 < 1
|
||||||
|
tb3 mybool = ti1 /* ERROR "cannot compare" */ == tf1
|
||||||
|
|
||||||
|
// integer values
|
||||||
|
ti0 int8 = ui0
|
||||||
|
ti1 int32 = ui1
|
||||||
|
ti2 int64 = ui2
|
||||||
|
ti3 myint = ui3 /* ERROR "overflows" */
|
||||||
|
ti4 myint = ui4
|
||||||
|
|
||||||
|
ti5 = ti0 /* ERROR "mismatched types" */ + ti1
|
||||||
|
ti6 = ti1 - ti1
|
||||||
|
ti7 = ti2 /* ERROR "mismatched types" */ * ti1
|
||||||
|
ti8 = ti3 / ti3
|
||||||
|
ti9 = ti3 % ti3
|
||||||
|
|
||||||
|
ti10 = 1 / 0 /* ERROR "division by zero" */
|
||||||
|
ti11 = ti1 / 0 /* ERROR "division by zero" */
|
||||||
|
ti12 = ti3 /* ERROR "mismatched types" */ / ti0
|
||||||
|
ti13 = 1 % 0 /* ERROR "division by zero" */
|
||||||
|
ti14 = ti1 % 0 /* ERROR "division by zero" */
|
||||||
|
ti15 = ti3 /* ERROR "mismatched types" */ % ti0
|
||||||
|
|
||||||
|
ti16 = ti2 /* ERROR "mismatched types" */ & ti3
|
||||||
|
ti17 = ti2 /* ERROR "mismatched types" */ | ti4
|
||||||
|
ti18 = ti2 ^ ti5 // no mismatched types error because the type of ti5 is unknown
|
||||||
|
|
||||||
|
// floating point values
|
||||||
|
tf0 float32 = 0.
|
||||||
|
tf1 float32 = 1.
|
||||||
|
tf2 float64 = 4.2e1
|
||||||
|
tf3 myfloat = 3.141592653589793238462643383279502884197169399375105820974944592307816406286
|
||||||
|
tf4 myfloat = 1e-1
|
||||||
|
|
||||||
|
tf5 = tf0 + tf1
|
||||||
|
tf6 = tf1 - tf1
|
||||||
|
tf7 = tf2 /* ERROR "mismatched types" */ * tf1
|
||||||
|
tf8 = tf3 / tf3
|
||||||
|
tf9 = tf3 /* ERROR "not defined" */ % tf3
|
||||||
|
|
||||||
|
tf10 = 1 / 0 /* ERROR "division by zero" */
|
||||||
|
tf11 = tf1 / 0 /* ERROR "division by zero" */
|
||||||
|
tf12 = tf3 /* ERROR "mismatched types" */ / tf0
|
||||||
|
|
||||||
|
tf16 = tf2 /* ERROR "mismatched types" */ & tf3
|
||||||
|
tf17 = tf2 /* ERROR "mismatched types" */ | tf3
|
||||||
|
tf18 = tf2 /* ERROR "mismatched types" */ ^ tf3
|
||||||
|
|
||||||
|
// complex values
|
||||||
|
tc0 = 0.i
|
||||||
|
tc1 = 1.i
|
||||||
|
tc2 = 4.2e1i
|
||||||
|
tc3 = 3.141592653589793238462643383279502884197169399375105820974944592307816406286i
|
||||||
|
tc4 = 1e-1i
|
||||||
|
|
||||||
|
tc5 = tc0 + tc1
|
||||||
|
tc6 = tc1 - tc1
|
||||||
|
tc7 = tc2 * tc1
|
||||||
|
tc8 = tc3 / tc3
|
||||||
|
tc9 = tc3 /* ERROR "not defined" */ % tc3
|
||||||
|
|
||||||
|
tc10 = 1 / 0 /* ERROR "division by zero" */
|
||||||
|
tc11 = tc1 / 0 /* ERROR "division by zero" */
|
||||||
|
tc12 = tc3 / tc0 /* ERROR "division by zero" */
|
||||||
|
|
||||||
|
tc16 = tc2 /* ERROR "not defined" */ & tc3
|
||||||
|
tc17 = tc2 /* ERROR "not defined" */ | tc3
|
||||||
|
tc18 = tc2 /* ERROR "not defined" */ ^ tc3
|
||||||
|
)
|
||||||
|
|
||||||
|
// initialization cycles
|
||||||
|
const (
|
||||||
|
a /* ERROR "cycle" */ = a
|
||||||
|
b /* ERROR "cycle" */ , c /* ERROR "cycle" */, d, e = e, d, c, b // TODO(gri) should only have one cycle error
|
||||||
|
f float64 = d
|
||||||
|
)
|
||||||
|
|
||||||
|
// multiple initialization
|
||||||
|
const (
|
||||||
|
a1, a2, a3 = 7, 3.1415926, "foo"
|
||||||
|
b1, b2, b3 = b3, b1, 42
|
||||||
|
_p0 = assert(a1 == 7)
|
||||||
|
_p1 = assert(a2 == 3.1415926)
|
||||||
|
_p2 = assert(a3 == "foo")
|
||||||
|
_p3 = assert(b1 == 42)
|
||||||
|
_p4 = assert(b2 == 42)
|
||||||
|
_p5 = assert(b3 == 42)
|
||||||
|
)
|
||||||
|
|
||||||
|
// iota
|
||||||
|
const (
|
||||||
|
iota0 = iota
|
||||||
|
iota1 = iota
|
||||||
|
iota2 = iota*2
|
||||||
|
_a0 = assert(iota0 == 0)
|
||||||
|
_a1 = assert(iota1 == 1)
|
||||||
|
_a2 = assert(iota2 == 4)
|
||||||
|
iota6 = iota*3
|
||||||
|
|
||||||
|
iota7
|
||||||
|
iota8
|
||||||
|
_a3 = assert(iota7 == 21)
|
||||||
|
_a4 = assert(iota8 == 24)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_b0 = iota
|
||||||
|
_b1 = assert(iota + iota2 == 5)
|
||||||
|
)
|
||||||
|
|
||||||
|
// special cases
|
||||||
|
const (
|
||||||
|
_n0 = nil /* ERROR "invalid constant type" */
|
||||||
|
_n1 = [ /* ERROR "not constant" */ ]int{}
|
||||||
|
)
|
36
go/types/testdata/conversions.src
vendored
Normal file
36
go/types/testdata/conversions.src
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// conversions
|
||||||
|
|
||||||
|
package conversions
|
||||||
|
|
||||||
|
// argument count
|
||||||
|
var (
|
||||||
|
_ = int /* ERROR "one argument" */ ()
|
||||||
|
_ = int /* ERROR "one argument" */ (1, 2)
|
||||||
|
)
|
||||||
|
|
||||||
|
func string_conversions() {
|
||||||
|
const A = string(65)
|
||||||
|
assert(A == "A")
|
||||||
|
const E = string(-1)
|
||||||
|
assert(E == "\uFFFD")
|
||||||
|
assert(E == string(1234567890))
|
||||||
|
|
||||||
|
type myint int
|
||||||
|
assert(A == string(myint(65)))
|
||||||
|
|
||||||
|
type mystring string
|
||||||
|
const _ mystring = mystring("foo")
|
||||||
|
|
||||||
|
const _ = string /* ERROR "cannot convert" */ (true)
|
||||||
|
const _ = string /* ERROR "cannot convert" */ (1.2)
|
||||||
|
const _ = string /* ERROR "cannot convert" */ (nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
var (
|
||||||
|
_ = int8(0)
|
||||||
|
)
|
187
go/types/testdata/decls0.src
vendored
Normal file
187
go/types/testdata/decls0.src
vendored
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
// Copyright 2011 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.
|
||||||
|
|
||||||
|
// type declarations
|
||||||
|
|
||||||
|
package decls0
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
// we can have multiple blank imports (was bug)
|
||||||
|
_ "math"
|
||||||
|
_ "net/rpc"
|
||||||
|
// reflect defines a type "flag" which shows up in the gc export data
|
||||||
|
"reflect"
|
||||||
|
. "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// reflect.flag must not be visible in this package
|
||||||
|
type flag int
|
||||||
|
type _ reflect /* ERROR "not exported" */ .flag
|
||||||
|
|
||||||
|
// dot-imported exported objects may conflict with local objects
|
||||||
|
type Value /* ERROR "redeclared in this block by dot-import" */ struct{}
|
||||||
|
|
||||||
|
const pi = 3.1415
|
||||||
|
|
||||||
|
type (
|
||||||
|
N undeclared /* ERROR "undeclared" */
|
||||||
|
B bool
|
||||||
|
I int32
|
||||||
|
A [10]P
|
||||||
|
T struct {
|
||||||
|
x, y P
|
||||||
|
}
|
||||||
|
P *T
|
||||||
|
R (*R)
|
||||||
|
F func(A) I
|
||||||
|
Y interface {
|
||||||
|
f(A) I
|
||||||
|
}
|
||||||
|
S [](((P)))
|
||||||
|
M map[I]F
|
||||||
|
C chan<- I
|
||||||
|
|
||||||
|
// blank types must be typechecked
|
||||||
|
_ pi /* ERROR "not a type" */
|
||||||
|
_ struct{}
|
||||||
|
_ struct{ pi /* ERROR "not a type" */ }
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// invalid array types
|
||||||
|
type (
|
||||||
|
iA0 [... /* ERROR "invalid use of '...'" */ ]byte
|
||||||
|
iA1 [1 /* ERROR "invalid array length" */ <<100]int
|
||||||
|
iA2 [- /* ERROR "invalid array length" */ 1]complex128
|
||||||
|
iA3 ["foo" /* ERROR "must be integer" */ ]string
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type (
|
||||||
|
p1 pi /* ERROR "no single field or method foo" */ .foo
|
||||||
|
p2 unsafe.Pointer
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type (
|
||||||
|
Pi pi /* ERROR "not a type" */
|
||||||
|
|
||||||
|
a /* ERROR "illegal cycle" */ a
|
||||||
|
a /* ERROR "redeclared" */ int
|
||||||
|
|
||||||
|
// where the cycle error appears depends on the
|
||||||
|
// order in which declarations are processed
|
||||||
|
// (which depends on the order in which a map
|
||||||
|
// is iterated through)
|
||||||
|
b /* ERROR "illegal cycle" */ c
|
||||||
|
c d
|
||||||
|
d e
|
||||||
|
e b
|
||||||
|
|
||||||
|
t *t
|
||||||
|
|
||||||
|
U V
|
||||||
|
V *W
|
||||||
|
W U
|
||||||
|
|
||||||
|
P1 *S2
|
||||||
|
P2 P1
|
||||||
|
|
||||||
|
S0 struct {
|
||||||
|
}
|
||||||
|
S1 struct {
|
||||||
|
a, b, c int
|
||||||
|
u, v, a /* ERROR "redeclared" */ float32
|
||||||
|
}
|
||||||
|
S2 struct {
|
||||||
|
U // anonymous field
|
||||||
|
// TODO(gri) recognize double-declaration below
|
||||||
|
// U /* ERROR "redeclared" */ int
|
||||||
|
}
|
||||||
|
S3 struct {
|
||||||
|
x S2
|
||||||
|
}
|
||||||
|
S4/* ERROR "illegal cycle" */ struct {
|
||||||
|
S4
|
||||||
|
}
|
||||||
|
S5 /* ERROR "illegal cycle" */ struct {
|
||||||
|
S6
|
||||||
|
}
|
||||||
|
S6 struct {
|
||||||
|
field S7
|
||||||
|
}
|
||||||
|
S7 struct {
|
||||||
|
S5
|
||||||
|
}
|
||||||
|
|
||||||
|
L1 []L1
|
||||||
|
L2 []int
|
||||||
|
|
||||||
|
A1 [10.0]int
|
||||||
|
A2 /* ERROR "illegal cycle" */ [10]A2
|
||||||
|
A3 /* ERROR "illegal cycle" */ [10]struct {
|
||||||
|
x A4
|
||||||
|
}
|
||||||
|
A4 [10]A3
|
||||||
|
|
||||||
|
F1 func()
|
||||||
|
F2 func(x, y, z float32)
|
||||||
|
F3 func(x, y, x /* ERROR "redeclared" */ float32)
|
||||||
|
F4 func() (x, y, x /* ERROR "redeclared" */ float32)
|
||||||
|
F5 func(x int) (x /* ERROR "redeclared" */ float32)
|
||||||
|
F6 func(x ...int)
|
||||||
|
|
||||||
|
I1 interface{}
|
||||||
|
I2 interface {
|
||||||
|
m1()
|
||||||
|
}
|
||||||
|
I3 interface { /* ERROR "multiple methods named m1" */
|
||||||
|
m1()
|
||||||
|
m1 /* ERROR "redeclared" */ ()
|
||||||
|
}
|
||||||
|
I4 interface {
|
||||||
|
m1(x, y, x /* ERROR "redeclared" */ float32)
|
||||||
|
m2() (x, y, x /* ERROR "redeclared" */ float32)
|
||||||
|
m3(x int) (x /* ERROR "redeclared" */ float32)
|
||||||
|
}
|
||||||
|
I5 interface {
|
||||||
|
m1(I5)
|
||||||
|
}
|
||||||
|
I6 interface {
|
||||||
|
S0 /* ERROR "not an interface" */
|
||||||
|
}
|
||||||
|
I7 interface {
|
||||||
|
I1
|
||||||
|
I1
|
||||||
|
}
|
||||||
|
I8 /* ERROR "illegal cycle" */ interface {
|
||||||
|
I8
|
||||||
|
}
|
||||||
|
// Use I09 (rather than I9) because it appears lexically before
|
||||||
|
// I10 so that we get the illegal cycle here rather then in the
|
||||||
|
// declaration of I10. If the implementation sorts by position
|
||||||
|
// rather than name, the error message will still be here.
|
||||||
|
I09 /* ERROR "illegal cycle" */ interface {
|
||||||
|
I10
|
||||||
|
}
|
||||||
|
I10 interface {
|
||||||
|
I11
|
||||||
|
}
|
||||||
|
I11 interface {
|
||||||
|
I09
|
||||||
|
}
|
||||||
|
|
||||||
|
C1 chan int
|
||||||
|
C2 <-chan int
|
||||||
|
C3 chan<- C3
|
||||||
|
C4 chan C5
|
||||||
|
C5 chan C6
|
||||||
|
C6 chan C4
|
||||||
|
|
||||||
|
M1 map[Last]string
|
||||||
|
M2 map[string]M2
|
||||||
|
|
||||||
|
Last int
|
||||||
|
)
|
132
go/types/testdata/decls1.src
vendored
Normal file
132
go/types/testdata/decls1.src
vendored
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// variable declarations
|
||||||
|
|
||||||
|
package decls1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Global variables without initialization
|
||||||
|
var (
|
||||||
|
a, b bool
|
||||||
|
c byte
|
||||||
|
d uint8
|
||||||
|
r rune
|
||||||
|
i int
|
||||||
|
j, k, l int
|
||||||
|
x, y float32
|
||||||
|
xx, yy float64
|
||||||
|
u, v complex64
|
||||||
|
uu, vv complex128
|
||||||
|
s, t string
|
||||||
|
array []byte
|
||||||
|
iface interface{}
|
||||||
|
|
||||||
|
blank _ /* ERROR "cannot use _" */
|
||||||
|
)
|
||||||
|
|
||||||
|
// Global variables with initialization
|
||||||
|
var (
|
||||||
|
s1 = i + j
|
||||||
|
s2 = i /* ERROR "mismatched types" */ + x
|
||||||
|
s3 = c + d
|
||||||
|
s4 = s + t
|
||||||
|
s5 = s /* ERROR "invalid operation" */ / t
|
||||||
|
s6 = array[t1]
|
||||||
|
s7 = array[x /* ERROR "integer" */]
|
||||||
|
s8 = &a
|
||||||
|
s10 = &42 /* ERROR "cannot take address" */
|
||||||
|
s11 = &v
|
||||||
|
s12 = -(u + *t11) / *&v
|
||||||
|
s13 = a /* ERROR "shifted operand" */ << d
|
||||||
|
s14 = i << j /* ERROR "must be unsigned" */
|
||||||
|
s18 = math.Pi * 10.0
|
||||||
|
s19 = s1 /* ERROR "cannot call" */ ()
|
||||||
|
s20 = f0 /* ERROR "no value" */ ()
|
||||||
|
s21 = f6(1, s1, i)
|
||||||
|
s22 = f6(1, s1, uu /* ERROR "cannot pass argument" */ )
|
||||||
|
|
||||||
|
t1 int = i + j
|
||||||
|
t2 int = i /* ERROR "mismatched types" */ + x
|
||||||
|
t3 int = c /* ERROR "cannot initialize" */ + d
|
||||||
|
t4 string = s + t
|
||||||
|
t5 string = s /* ERROR "invalid operation" */ / t
|
||||||
|
t6 byte = array[t1]
|
||||||
|
t7 byte = array[x /* ERROR "must be integer" */]
|
||||||
|
t8 *int = & /* ERROR "cannot initialize" */ a
|
||||||
|
t10 *int = &42 /* ERROR "cannot take address" */
|
||||||
|
t11 *complex64 = &v
|
||||||
|
t12 complex64 = -(u + *t11) / *&v
|
||||||
|
t13 int = a /* ERROR "shifted operand" */ << d
|
||||||
|
t14 int = i << j /* ERROR "must be unsigned" */
|
||||||
|
t15 math /* ERROR "not in selector" */
|
||||||
|
t16 math /* ERROR "not declared" */ .xxx
|
||||||
|
t17 math /* ERROR "not a type" */ .Pi
|
||||||
|
t18 float64 = math.Pi * 10.0
|
||||||
|
t19 int = t1 /* ERROR "cannot call" */ ()
|
||||||
|
t20 int = f0 /* ERROR "no value" */ ()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Various more complex expressions
|
||||||
|
var (
|
||||||
|
u1 = x /* ERROR "not an interface" */ .(int)
|
||||||
|
u2 = iface.([]int)
|
||||||
|
u3 = iface.(a /* ERROR "not a type" */ )
|
||||||
|
u4, ok = iface.(int)
|
||||||
|
u5 /* ERROR "assignment count mismatch" */ , ok2, ok3 = iface.(int)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Constant expression initializations
|
||||||
|
var (
|
||||||
|
v1 = 1 /* ERROR "cannot convert" */ + "foo"
|
||||||
|
v2 = c + 255
|
||||||
|
v3 = c + 256 /* ERROR "overflows" */
|
||||||
|
v4 = r + 2147483647
|
||||||
|
v5 = r + 2147483648 /* ERROR "overflows" */
|
||||||
|
v6 = 42
|
||||||
|
v7 = v6 + 9223372036854775807
|
||||||
|
v8 = v6 + 9223372036854775808 /* ERROR "overflows" */
|
||||||
|
v9 = i + 1 << 10
|
||||||
|
v10 byte = 1024 /* ERROR "overflows" */
|
||||||
|
v11 = xx/yy*yy - xx
|
||||||
|
v12 = true && false
|
||||||
|
v13 = nil /* ERROR "use of untyped nil" */
|
||||||
|
)
|
||||||
|
|
||||||
|
// Multiple assignment expressions
|
||||||
|
var (
|
||||||
|
m1a, m1b = 1, 2
|
||||||
|
m2a /* ERROR "assignment count mismatch" */ , m2b, m2c = 1, 2
|
||||||
|
m3a /* ERROR "assignment count mismatch" */ , m3b = 1, 2, 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// Declaration of parameters and results
|
||||||
|
func f0() {}
|
||||||
|
func f1(a /* ERROR "not a type" */) {}
|
||||||
|
func f2(a, b, c d /* ERROR "not a type" */) {}
|
||||||
|
|
||||||
|
func f3() int { return 0 }
|
||||||
|
func f4() a /* ERROR "not a type" */ { return 0 /* ERROR "cannot convert" */ }
|
||||||
|
func f5() (a, b, c d /* ERROR "not a type" */) { return }
|
||||||
|
|
||||||
|
func f6(a, b, c int) complex128 { return 0 }
|
||||||
|
|
||||||
|
// Declaration of receivers
|
||||||
|
type T struct{}
|
||||||
|
|
||||||
|
func (T) m0() {}
|
||||||
|
func (*T) m1() {}
|
||||||
|
func (x T) m2() {}
|
||||||
|
func (x *T) m3() {}
|
||||||
|
|
||||||
|
|
||||||
|
// Initialization functions
|
||||||
|
func init() {}
|
||||||
|
func /* ERROR "no arguments and no return values" */ init(int) {}
|
||||||
|
func /* ERROR "no arguments and no return values" */ init() int { return 0 }
|
||||||
|
func /* ERROR "no arguments and no return values" */ init(int) int { return 0 }
|
||||||
|
func (T) init(int) int { return 0 }
|
66
go/types/testdata/decls2a.src
vendored
Normal file
66
go/types/testdata/decls2a.src
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// method declarations
|
||||||
|
|
||||||
|
package decls2
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// T1 declared before its methods.
|
||||||
|
type T1 struct{
|
||||||
|
f int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (T1) m() {}
|
||||||
|
func (T1) m /* ERROR "redeclared" */ () {}
|
||||||
|
func (x *T1) f /* ERROR "field and method" */ () {}
|
||||||
|
|
||||||
|
// T2's method declared before the type.
|
||||||
|
func (*T2) f /* ERROR "field and method" */ () {}
|
||||||
|
|
||||||
|
type T2 struct {
|
||||||
|
f int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods declared without a declared type.
|
||||||
|
func (undeclared /* ERROR "undeclared" */) m() {}
|
||||||
|
func (x *undeclared /* ERROR "undeclared" */) m() {}
|
||||||
|
|
||||||
|
func (pi /* ERROR "not a type" */) m1() {}
|
||||||
|
func (x pi /* ERROR "not a type" */) m2() {}
|
||||||
|
func (x *pi /* ERROR "not a type" */ ) m3() {}
|
||||||
|
|
||||||
|
// Blank types.
|
||||||
|
type _ struct { m int }
|
||||||
|
type _ struct { m int }
|
||||||
|
|
||||||
|
// TODO(gri) blank idents not fully checked - disabled for now
|
||||||
|
// func (_ /* ERROR "cannot use _" */) m() {}
|
||||||
|
// func (_ /* ERROR "cannot use _" */) m() {}
|
||||||
|
|
||||||
|
// Methods with receiver base type declared in another file.
|
||||||
|
func (T3) m1() {}
|
||||||
|
func (*T3) m2() {}
|
||||||
|
func (x T3) m3() {}
|
||||||
|
func (x *T3) f /* ERROR "field and method" */ () {}
|
||||||
|
|
||||||
|
// Methods of non-struct type.
|
||||||
|
type T4 func()
|
||||||
|
|
||||||
|
func (self T4) m() func() { return self }
|
||||||
|
|
||||||
|
// Methods associated with an interface.
|
||||||
|
type T5 interface {
|
||||||
|
m() int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (T5 /* ERROR "invalid receiver" */) m1() {}
|
||||||
|
func (T5 /* ERROR "invalid receiver" */) m2() {}
|
||||||
|
|
||||||
|
// Methods associated with non-local or unnamed types.
|
||||||
|
func (int /* ERROR "non-local type" */ ) m() {}
|
||||||
|
func ([ /* ERROR "expected" */ ]int) m() {}
|
||||||
|
func (time /* ERROR "expected" */ .Time) m() {}
|
||||||
|
func (x interface /* ERROR "expected" */ {}) m() {}
|
28
go/types/testdata/decls2b.src
vendored
Normal file
28
go/types/testdata/decls2b.src
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// method declarations
|
||||||
|
|
||||||
|
package decls2
|
||||||
|
|
||||||
|
const pi = 3.1415
|
||||||
|
|
||||||
|
func (T1) m /* ERROR "redeclared" */ () {}
|
||||||
|
|
||||||
|
type T3 struct {
|
||||||
|
f *T3
|
||||||
|
}
|
||||||
|
|
||||||
|
type T6 struct {
|
||||||
|
x int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *T6) m1() int {
|
||||||
|
return t.x
|
||||||
|
}
|
||||||
|
|
||||||
|
func f() {
|
||||||
|
var t *T6
|
||||||
|
t.m1()
|
||||||
|
}
|
253
go/types/testdata/decls3.src
vendored
Normal file
253
go/types/testdata/decls3.src
vendored
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// embedded types
|
||||||
|
|
||||||
|
package decls3
|
||||||
|
|
||||||
|
// fields with the same name at the same level cancel each other out
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
type (
|
||||||
|
T1 struct { X int }
|
||||||
|
T2 struct { X int }
|
||||||
|
T3 struct { T1; T2 } // X is embedded twice at the same level via T1->X, T2->X
|
||||||
|
)
|
||||||
|
|
||||||
|
var t T3
|
||||||
|
_ = t /* ERROR "no single field or method" */ .X
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
type (
|
||||||
|
T1 struct { X int }
|
||||||
|
T2 struct { T1 }
|
||||||
|
T3 struct { T1 }
|
||||||
|
T4 struct { T2; T3 } // X is embedded twice at the same level via T2->T1->X, T3->T1->X
|
||||||
|
)
|
||||||
|
|
||||||
|
var t T4
|
||||||
|
_ = t /* ERROR "no single field or method" */ .X
|
||||||
|
}
|
||||||
|
|
||||||
|
func issue4355() {
|
||||||
|
type (
|
||||||
|
T1 struct {X int}
|
||||||
|
T2 struct {T1}
|
||||||
|
T3 struct {T2}
|
||||||
|
T4 struct {T2}
|
||||||
|
T5 struct {T3; T4} // X is embedded twice at the same level via T3->T2->T1->X, T4->T2->T1->X
|
||||||
|
)
|
||||||
|
|
||||||
|
var t T5
|
||||||
|
_ = t /* ERROR "no single field or method" */ .X
|
||||||
|
}
|
||||||
|
|
||||||
|
// Embedded fields can be predeclared types.
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
type T0 struct{
|
||||||
|
int
|
||||||
|
float32
|
||||||
|
f int
|
||||||
|
}
|
||||||
|
var x T0
|
||||||
|
_ = x.int
|
||||||
|
_ = x.float32
|
||||||
|
_ = x.f
|
||||||
|
|
||||||
|
type T1 struct{
|
||||||
|
T0
|
||||||
|
}
|
||||||
|
var y T1
|
||||||
|
_ = y.int
|
||||||
|
_ = y.float32
|
||||||
|
_ = y.f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Borrowed from the FieldByName test cases in reflect/all_test.go.
|
||||||
|
|
||||||
|
type D1 struct {
|
||||||
|
d int
|
||||||
|
}
|
||||||
|
type D2 struct {
|
||||||
|
d int
|
||||||
|
}
|
||||||
|
|
||||||
|
type S0 struct {
|
||||||
|
A, B, C int
|
||||||
|
D1
|
||||||
|
D2
|
||||||
|
}
|
||||||
|
|
||||||
|
type S1 struct {
|
||||||
|
B int
|
||||||
|
S0
|
||||||
|
}
|
||||||
|
|
||||||
|
type S2 struct {
|
||||||
|
A int
|
||||||
|
*S1
|
||||||
|
}
|
||||||
|
|
||||||
|
type S1x struct {
|
||||||
|
S1
|
||||||
|
}
|
||||||
|
|
||||||
|
type S1y struct {
|
||||||
|
S1
|
||||||
|
}
|
||||||
|
|
||||||
|
type S3 struct {
|
||||||
|
S1x
|
||||||
|
S2
|
||||||
|
D, E int
|
||||||
|
*S1y
|
||||||
|
}
|
||||||
|
|
||||||
|
type S4 struct {
|
||||||
|
*S4
|
||||||
|
A int
|
||||||
|
}
|
||||||
|
|
||||||
|
// The X in S6 and S7 annihilate, but they also block the X in S8.S9.
|
||||||
|
type S5 struct {
|
||||||
|
S6
|
||||||
|
S7
|
||||||
|
S8
|
||||||
|
}
|
||||||
|
|
||||||
|
type S6 struct {
|
||||||
|
X int
|
||||||
|
}
|
||||||
|
|
||||||
|
type S7 S6
|
||||||
|
|
||||||
|
type S8 struct {
|
||||||
|
S9
|
||||||
|
}
|
||||||
|
|
||||||
|
type S9 struct {
|
||||||
|
X int
|
||||||
|
Y int
|
||||||
|
}
|
||||||
|
|
||||||
|
// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9.
|
||||||
|
type S10 struct {
|
||||||
|
S11
|
||||||
|
S12
|
||||||
|
S13
|
||||||
|
}
|
||||||
|
|
||||||
|
type S11 struct {
|
||||||
|
S6
|
||||||
|
}
|
||||||
|
|
||||||
|
type S12 struct {
|
||||||
|
S6
|
||||||
|
}
|
||||||
|
|
||||||
|
type S13 struct {
|
||||||
|
S8
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
_ = struct /* ERROR "no single field or method" */ {}{}.Foo
|
||||||
|
_ = S0{}.A
|
||||||
|
_ = S0 /* ERROR "no single field or method" */ {}.D
|
||||||
|
_ = S1{}.A
|
||||||
|
_ = S1{}.B
|
||||||
|
_ = S1{}.S0
|
||||||
|
_ = S1{}.C
|
||||||
|
_ = S2{}.A
|
||||||
|
_ = S2{}.S1
|
||||||
|
_ = S2{}.B
|
||||||
|
_ = S2{}.C
|
||||||
|
_ = S2 /* ERROR "no single field or method" */ {}.D
|
||||||
|
_ = S3 /* ERROR "no single field or method" */ {}.S1
|
||||||
|
_ = S3{}.A
|
||||||
|
_ = S3 /* ERROR "no single field or method" */ {}.B
|
||||||
|
_ = S3{}.D
|
||||||
|
_ = S3{}.E
|
||||||
|
_ = S4{}.A
|
||||||
|
_ = S4 /* ERROR "no single field or method" */ {}.B
|
||||||
|
_ = S5 /* ERROR "no single field or method" */ {}.X
|
||||||
|
_ = S5{}.Y
|
||||||
|
_ = S10 /* ERROR "no single field or method" */ {}.X
|
||||||
|
_ = S10{}.Y
|
||||||
|
}
|
||||||
|
|
||||||
|
// Borrowed from the FieldByName benchmark in reflect/all_test.go.
|
||||||
|
|
||||||
|
type R0 struct {
|
||||||
|
*R1
|
||||||
|
*R2
|
||||||
|
*R3
|
||||||
|
*R4
|
||||||
|
}
|
||||||
|
|
||||||
|
type R1 struct {
|
||||||
|
*R5
|
||||||
|
*R6
|
||||||
|
*R7
|
||||||
|
*R8
|
||||||
|
}
|
||||||
|
|
||||||
|
type R2 R1
|
||||||
|
type R3 R1
|
||||||
|
type R4 R1
|
||||||
|
|
||||||
|
type R5 struct {
|
||||||
|
*R9
|
||||||
|
*R10
|
||||||
|
*R11
|
||||||
|
*R12
|
||||||
|
}
|
||||||
|
|
||||||
|
type R6 R5
|
||||||
|
type R7 R5
|
||||||
|
type R8 R5
|
||||||
|
|
||||||
|
type R9 struct {
|
||||||
|
*R13
|
||||||
|
*R14
|
||||||
|
*R15
|
||||||
|
*R16
|
||||||
|
}
|
||||||
|
|
||||||
|
type R10 R9
|
||||||
|
type R11 R9
|
||||||
|
type R12 R9
|
||||||
|
|
||||||
|
type R13 struct {
|
||||||
|
*R17
|
||||||
|
*R18
|
||||||
|
*R19
|
||||||
|
*R20
|
||||||
|
}
|
||||||
|
|
||||||
|
type R14 R13
|
||||||
|
type R15 R13
|
||||||
|
type R16 R13
|
||||||
|
|
||||||
|
type R17 struct {
|
||||||
|
*R21
|
||||||
|
*R22
|
||||||
|
*R23
|
||||||
|
*R24
|
||||||
|
}
|
||||||
|
|
||||||
|
type R18 R17
|
||||||
|
type R19 R17
|
||||||
|
type R20 R17
|
||||||
|
|
||||||
|
type R21 struct {
|
||||||
|
X int
|
||||||
|
}
|
||||||
|
|
||||||
|
type R22 R21
|
||||||
|
type R23 R21
|
||||||
|
type R24 R21
|
||||||
|
|
||||||
|
var _ = R0 /* ERROR "no single field or method" */ {}.X
|
89
go/types/testdata/exports.go
vendored
Normal file
89
go/types/testdata/exports.go
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// Copyright 2011 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 is used to generate an object file which
|
||||||
|
// serves as test file for gcimporter_test.go.
|
||||||
|
|
||||||
|
package exports
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Issue 3682: Correctly read dotted identifiers from export data.
|
||||||
|
const init1 = 0
|
||||||
|
|
||||||
|
func init() {}
|
||||||
|
|
||||||
|
const (
|
||||||
|
C0 int = 0
|
||||||
|
C1 = 3.14159265
|
||||||
|
C2 = 2.718281828i
|
||||||
|
C3 = -123.456e-789
|
||||||
|
C4 = +123.456E+789
|
||||||
|
C5 = 1234i
|
||||||
|
C6 = "foo\n"
|
||||||
|
C7 = `bar\n`
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
T1 int
|
||||||
|
T2 [10]int
|
||||||
|
T3 []int
|
||||||
|
T4 *int
|
||||||
|
T5 chan int
|
||||||
|
T6a chan<- int
|
||||||
|
T6b chan (<-chan int)
|
||||||
|
T6c chan<- (chan int)
|
||||||
|
T7 <-chan *ast.File
|
||||||
|
T8 struct{}
|
||||||
|
T9 struct {
|
||||||
|
a int
|
||||||
|
b, c float32
|
||||||
|
d []string `go:"tag"`
|
||||||
|
}
|
||||||
|
T10 struct {
|
||||||
|
T8
|
||||||
|
T9
|
||||||
|
_ *T10
|
||||||
|
}
|
||||||
|
T11 map[int]string
|
||||||
|
T12 interface{}
|
||||||
|
T13 interface {
|
||||||
|
m1()
|
||||||
|
m2(int) float32
|
||||||
|
}
|
||||||
|
T14 interface {
|
||||||
|
T12
|
||||||
|
T13
|
||||||
|
m3(x ...struct{}) []T9
|
||||||
|
}
|
||||||
|
T15 func()
|
||||||
|
T16 func(int)
|
||||||
|
T17 func(x int)
|
||||||
|
T18 func() float32
|
||||||
|
T19 func() (x float32)
|
||||||
|
T20 func(...interface{})
|
||||||
|
T21 struct{ next *T21 }
|
||||||
|
T22 struct{ link *T23 }
|
||||||
|
T23 struct{ link *T22 }
|
||||||
|
T24 *T24
|
||||||
|
T25 *T26
|
||||||
|
T26 *T27
|
||||||
|
T27 *T25
|
||||||
|
T28 func(T28) T28
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
V0 int
|
||||||
|
V1 = -991.0
|
||||||
|
)
|
||||||
|
|
||||||
|
func F1() {}
|
||||||
|
func F2(x int) {}
|
||||||
|
func F3() int { return 0 }
|
||||||
|
func F4() float32 { return 0 }
|
||||||
|
func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...interface{}) (p, q, r chan<- T10)
|
||||||
|
|
||||||
|
func (p *T1) M1()
|
161
go/types/testdata/expr0.src
vendored
Normal file
161
go/types/testdata/expr0.src
vendored
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// unary expressions
|
||||||
|
|
||||||
|
package expr0
|
||||||
|
|
||||||
|
var (
|
||||||
|
// bool
|
||||||
|
b0 = true
|
||||||
|
b1 bool = b0
|
||||||
|
b2 = !true
|
||||||
|
b3 = !b1
|
||||||
|
b4 bool = !true
|
||||||
|
b5 bool = !b4
|
||||||
|
b6 = +b0 /* ERROR "not defined" */
|
||||||
|
b7 = -b0 /* ERROR "not defined" */
|
||||||
|
b8 = ^b0 /* ERROR "not defined" */
|
||||||
|
b9 = *b0 /* ERROR "cannot indirect" */
|
||||||
|
b10 = &true /* ERROR "cannot take address" */
|
||||||
|
b11 = &b0
|
||||||
|
b12 = <-b0 /* ERROR "cannot receive" */
|
||||||
|
|
||||||
|
// int
|
||||||
|
i0 = 1
|
||||||
|
i1 int = i0
|
||||||
|
i2 = +1
|
||||||
|
i3 = +i0
|
||||||
|
i4 int = +1
|
||||||
|
i5 int = +i4
|
||||||
|
i6 = -1
|
||||||
|
i7 = -i0
|
||||||
|
i8 int = -1
|
||||||
|
i9 int = -i4
|
||||||
|
i10 = !i0 /* ERROR "not defined" */
|
||||||
|
i11 = ^1
|
||||||
|
i12 = ^i0
|
||||||
|
i13 int = ^1
|
||||||
|
i14 int = ^i4
|
||||||
|
i15 = *i0 /* ERROR "cannot indirect" */
|
||||||
|
i16 = &i0
|
||||||
|
i17 = *i16
|
||||||
|
i18 = <-i16 /* ERROR "cannot receive" */
|
||||||
|
|
||||||
|
// uint
|
||||||
|
u0 = uint(1)
|
||||||
|
u1 uint = u0
|
||||||
|
u2 = +1
|
||||||
|
u3 = +u0
|
||||||
|
u4 uint = +1
|
||||||
|
u5 uint = +u4
|
||||||
|
u6 = -1
|
||||||
|
u7 = -u0
|
||||||
|
u8 uint = - /* ERROR "overflows" */ 1
|
||||||
|
u9 uint = -u4
|
||||||
|
u10 = !u0 /* ERROR "not defined" */
|
||||||
|
u11 = ^1
|
||||||
|
u12 = ^i0
|
||||||
|
u13 uint = ^ /* ERROR "overflows" */ 1
|
||||||
|
u14 uint = ^u4
|
||||||
|
u15 = *u0 /* ERROR "cannot indirect" */
|
||||||
|
u16 = &u0
|
||||||
|
u17 = *u16
|
||||||
|
u18 = <-u16 /* ERROR "cannot receive" */
|
||||||
|
u19 = ^uint(0)
|
||||||
|
|
||||||
|
// float64
|
||||||
|
f0 = float64(1)
|
||||||
|
f1 float64 = f0
|
||||||
|
f2 = +1
|
||||||
|
f3 = +f0
|
||||||
|
f4 float64 = +1
|
||||||
|
f5 float64 = +f4 /* ERROR not defined */
|
||||||
|
f6 = -1
|
||||||
|
f7 = -f0
|
||||||
|
f8 float64 = -1
|
||||||
|
f9 float64 = -f4
|
||||||
|
f10 = !f0 /* ERROR "not defined" */
|
||||||
|
f11 = ^1
|
||||||
|
f12 = ^i0
|
||||||
|
f13 float64 = ^1
|
||||||
|
f14 float64 = ^f4 /* ERROR "not defined" */
|
||||||
|
f15 = *f0 /* ERROR "cannot indirect" */
|
||||||
|
f16 = &f0
|
||||||
|
f17 = *u16
|
||||||
|
f18 = <-u16 /* ERROR "cannot receive" */
|
||||||
|
|
||||||
|
// complex128
|
||||||
|
c0 = complex128(1)
|
||||||
|
c1 complex128 = c0
|
||||||
|
c2 = +1
|
||||||
|
c3 = +c0
|
||||||
|
c4 complex128 = +1
|
||||||
|
c5 complex128 = +c4 /* ERROR not defined */
|
||||||
|
c6 = -1
|
||||||
|
c7 = -c0
|
||||||
|
c8 complex128 = -1
|
||||||
|
c9 complex128 = -c4
|
||||||
|
c10 = !c0 /* ERROR "not defined" */
|
||||||
|
c11 = ^1
|
||||||
|
c12 = ^i0
|
||||||
|
c13 complex128 = ^1
|
||||||
|
c14 complex128 = ^c4 /* ERROR "not defined" */
|
||||||
|
c15 = *c0 /* ERROR "cannot indirect" */
|
||||||
|
c16 = &c0
|
||||||
|
c17 = *u16
|
||||||
|
c18 = <-u16 /* ERROR "cannot receive" */
|
||||||
|
|
||||||
|
// string
|
||||||
|
s0 = "foo"
|
||||||
|
s1 = +"foo" /* ERROR "not defined" */
|
||||||
|
s2 = -s0 /* ERROR "not defined" */
|
||||||
|
s3 = !s0 /* ERROR "not defined" */
|
||||||
|
s4 = ^s0 /* ERROR "not defined" */
|
||||||
|
s5 = *s4 /* ERROR "cannot indirect" */
|
||||||
|
s6 = &s4
|
||||||
|
s7 = *s6
|
||||||
|
s8 = <-s7 /* ERROR "cannot receive" */
|
||||||
|
|
||||||
|
// channel
|
||||||
|
ch chan int
|
||||||
|
rc <-chan float64
|
||||||
|
sc chan <- string
|
||||||
|
ch0 = +ch /* ERROR "not defined" */
|
||||||
|
ch1 = -ch /* ERROR "not defined" */
|
||||||
|
ch2 = !ch /* ERROR "not defined" */
|
||||||
|
ch3 = ^ch /* ERROR "not defined" */
|
||||||
|
ch4 = *ch /* ERROR "cannot indirect" */
|
||||||
|
ch5 = &ch
|
||||||
|
ch6 = *ch5
|
||||||
|
ch7 = <-ch
|
||||||
|
ch8 = <-rc
|
||||||
|
ch9 = <-sc /* ERROR "cannot receive" */
|
||||||
|
)
|
||||||
|
|
||||||
|
// address of composite literals
|
||||||
|
type T struct{x, y int}
|
||||||
|
|
||||||
|
func f() T { return T{} }
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ = &T{1, 2}
|
||||||
|
_ = &[...]int{}
|
||||||
|
_ = &[]int{}
|
||||||
|
_ = &[]int{}
|
||||||
|
_ = &map[string]T{}
|
||||||
|
_ = &(T{1, 2})
|
||||||
|
_ = &((((T{1, 2}))))
|
||||||
|
_ = &f /* ERROR "cannot take address" */ ()
|
||||||
|
)
|
||||||
|
|
||||||
|
// recursive pointer types
|
||||||
|
type P *P
|
||||||
|
|
||||||
|
var (
|
||||||
|
p1 P = new(P)
|
||||||
|
p2 P = *p1
|
||||||
|
p3 P = &p2
|
||||||
|
)
|
||||||
|
|
7
go/types/testdata/expr1.src
vendored
Normal file
7
go/types/testdata/expr1.src
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// binary expressions
|
||||||
|
|
||||||
|
package expr1
|
23
go/types/testdata/expr2.src
vendored
Normal file
23
go/types/testdata/expr2.src
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// comparisons
|
||||||
|
|
||||||
|
package expr2
|
||||||
|
|
||||||
|
func _bool() {
|
||||||
|
const t = true == true
|
||||||
|
const f = true == false
|
||||||
|
_ = t /* ERROR "cannot compare" */ < f
|
||||||
|
_ = 0 /* ERROR "cannot convert" */ == t
|
||||||
|
var b bool
|
||||||
|
var x, y float32
|
||||||
|
b = x < y
|
||||||
|
_ = struct{b bool}{x < y}
|
||||||
|
}
|
||||||
|
|
||||||
|
// corner cases
|
||||||
|
var (
|
||||||
|
v0 = nil /* ERROR "cannot compare" */ == nil
|
||||||
|
)
|
338
go/types/testdata/expr3.src
vendored
Normal file
338
go/types/testdata/expr3.src
vendored
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
// Copyright 2012 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 expr3
|
||||||
|
|
||||||
|
func indexes() {
|
||||||
|
_ = 1 /* ERROR "cannot index" */ [0]
|
||||||
|
_ = indexes /* ERROR "cannot index" */ [0]
|
||||||
|
_ = ( /* ERROR "cannot slice" */ 12 + 3)[1:2]
|
||||||
|
|
||||||
|
var a [10]int
|
||||||
|
_ = a[true /* ERROR "cannot convert" */ ]
|
||||||
|
_ = a["foo" /* ERROR "cannot convert" */ ]
|
||||||
|
_ = a[1.1 /* ERROR "overflows" */ ]
|
||||||
|
_ = a[1.0]
|
||||||
|
_ = a[- /* ERROR "negative" */ 1]
|
||||||
|
_ = a[- /* ERROR "negative" */ 1 :]
|
||||||
|
_ = a[: - /* ERROR "negative" */ 1]
|
||||||
|
var a0 int
|
||||||
|
a0 = a[0]
|
||||||
|
var a1 int32
|
||||||
|
a1 = a /* ERROR "cannot assign" */ [1]
|
||||||
|
_ = a[9]
|
||||||
|
_ = a[10 /* ERROR "index .* out of bounds" */ ]
|
||||||
|
_ = a[1 /* ERROR "overflows" */ <<100]
|
||||||
|
_ = a[10:]
|
||||||
|
_ = a[:10]
|
||||||
|
_ = a[10:10]
|
||||||
|
_ = a[11 /* ERROR "index .* out of bounds" */ :]
|
||||||
|
_ = a[: 11 /* ERROR "index .* out of bounds" */ ]
|
||||||
|
_ = a[: 1 /* ERROR "overflows" */ <<100]
|
||||||
|
|
||||||
|
pa := &a
|
||||||
|
_ = pa[9]
|
||||||
|
_ = pa[10 /* ERROR "index .* out of bounds" */ ]
|
||||||
|
_ = pa[1 /* ERROR "overflows" */ <<100]
|
||||||
|
_ = pa[10:]
|
||||||
|
_ = pa[:10]
|
||||||
|
_ = pa[10:10]
|
||||||
|
_ = pa[11 /* ERROR "index .* out of bounds" */ :]
|
||||||
|
_ = pa[: 11 /* ERROR "index .* out of bounds" */ ]
|
||||||
|
_ = pa[: 1 /* ERROR "overflows" */ <<100]
|
||||||
|
|
||||||
|
var b [0]int
|
||||||
|
_ = b[0 /* ERROR "index .* out of bounds" */ ]
|
||||||
|
_ = b[:]
|
||||||
|
_ = b[0:]
|
||||||
|
_ = b[:0]
|
||||||
|
_ = b[0:0]
|
||||||
|
|
||||||
|
var s []int
|
||||||
|
_ = s[- /* ERROR "negative" */ 1]
|
||||||
|
_ = s[- /* ERROR "negative" */ 1 :]
|
||||||
|
_ = s[: - /* ERROR "negative" */ 1]
|
||||||
|
_ = s[0]
|
||||||
|
_ = s[1 : 2]
|
||||||
|
_ = s[2 /* ERROR "inverted slice range" */ : 1]
|
||||||
|
_ = s[2 :]
|
||||||
|
_ = s[: 1 /* ERROR "overflows" */ <<100]
|
||||||
|
_ = s[1 /* ERROR "overflows" */ <<100 :]
|
||||||
|
_ = s[1 /* ERROR "overflows" */ <<100 : 1 /* ERROR "overflows" */ <<100]
|
||||||
|
|
||||||
|
var t string
|
||||||
|
_ = t[- /* ERROR "negative" */ 1]
|
||||||
|
_ = t[- /* ERROR "negative" */ 1 :]
|
||||||
|
_ = t[: - /* ERROR "negative" */ 1]
|
||||||
|
var t0 byte
|
||||||
|
t0 = t[0]
|
||||||
|
var t1 rune
|
||||||
|
t1 = t /* ERROR "cannot assign" */ [2]
|
||||||
|
_ = ("foo" + "bar")[5]
|
||||||
|
_ = ("foo" + "bar")[6 /* ERROR "index .* out of bounds" */ ]
|
||||||
|
|
||||||
|
const c = "foo"
|
||||||
|
_ = c[- /* ERROR "negative" */ 1]
|
||||||
|
_ = c[- /* ERROR "negative" */ 1 :]
|
||||||
|
_ = c[: - /* ERROR "negative" */ 1]
|
||||||
|
var c0 byte
|
||||||
|
c0 = c[0]
|
||||||
|
var c2 float32
|
||||||
|
c2 = c /* ERROR "cannot assign" */ [2]
|
||||||
|
_ = c[3 /* ERROR "index .* out of bounds" */ ]
|
||||||
|
_ = ""[0 /* ERROR "index .* out of bounds" */ ]
|
||||||
|
|
||||||
|
_ = s[1<<30] // no compile-time error here
|
||||||
|
|
||||||
|
// issue 4913
|
||||||
|
type mystring string
|
||||||
|
var ss string
|
||||||
|
var ms mystring
|
||||||
|
var i, j int
|
||||||
|
ss = "foo"[1:2]
|
||||||
|
ss = "foo"[i:j]
|
||||||
|
ms = "foo" /* ERROR "cannot assign" */ [1:2]
|
||||||
|
ms = "foo" /* ERROR "cannot assign" */ [i:j]
|
||||||
|
}
|
||||||
|
|
||||||
|
type T struct {
|
||||||
|
x int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*T) m() {}
|
||||||
|
|
||||||
|
func method_expressions() {
|
||||||
|
_ = T /* ERROR "no single field or method" */ .a
|
||||||
|
_ = T /* ERROR "has no method" */ .x
|
||||||
|
_ = T.m
|
||||||
|
var f func(*T) = (*T).m
|
||||||
|
var g func(*T) = ( /* ERROR "cannot initialize" */ T).m
|
||||||
|
}
|
||||||
|
|
||||||
|
func struct_literals() {
|
||||||
|
type T0 struct {
|
||||||
|
a, b, c int
|
||||||
|
}
|
||||||
|
|
||||||
|
type T1 struct {
|
||||||
|
T0
|
||||||
|
a, b int
|
||||||
|
u float64
|
||||||
|
s string
|
||||||
|
}
|
||||||
|
|
||||||
|
// keyed elements
|
||||||
|
_ = T1{}
|
||||||
|
_ = T1{a: 0, 1 /* ERROR "mixture of .* elements" */ }
|
||||||
|
_ = T1{aa /* ERROR "unknown field" */ : 0}
|
||||||
|
_ = T1{1 /* ERROR "invalid field name" */ : 0}
|
||||||
|
_ = T1{a: 0, s: "foo", u: 0, a /* ERROR "duplicate field" */: 10}
|
||||||
|
_ = T1{a: "foo" /* ERROR "cannot convert" */ }
|
||||||
|
_ = T1{c /* ERROR "unknown field" */ : 0}
|
||||||
|
_ = T1{T0: { /* ERROR "missing type" */ }}
|
||||||
|
_ = T1{T0: T0{}}
|
||||||
|
_ = T1{T0 /* ERROR "invalid field name" */ .a: 0}
|
||||||
|
|
||||||
|
// unkeyed elements
|
||||||
|
_ = T0{1, 2, 3}
|
||||||
|
_ = T0{1, b /* ERROR "mixture" */ : 2, 3}
|
||||||
|
_ = T0{1, 2} /* ERROR "too few values" */
|
||||||
|
_ = T0{1, 2, 3, 4 /* ERROR "too many values" */ }
|
||||||
|
_ = T0{1, "foo" /* ERROR "cannot convert" */, 3.4 /* ERROR "overflows" */}
|
||||||
|
}
|
||||||
|
|
||||||
|
func array_literals() {
|
||||||
|
type A0 [0]int
|
||||||
|
_ = A0{}
|
||||||
|
_ = A0{0 /* ERROR "index .* out of bounds" */}
|
||||||
|
_ = A0{0 /* ERROR "index .* out of bounds" */ : 0}
|
||||||
|
|
||||||
|
type A1 [10]int
|
||||||
|
_ = A1{}
|
||||||
|
_ = A1{0, 1, 2}
|
||||||
|
_ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||||
|
_ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /* ERROR "index .* out of bounds" */ }
|
||||||
|
_ = A1{- /* ERROR "negative" */ 1: 0}
|
||||||
|
_ = A1{8: 8, 9}
|
||||||
|
_ = A1{8: 8, 9, 10 /* ERROR "index .* out of bounds" */ }
|
||||||
|
_ = A1{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
|
||||||
|
_ = A1{5: 5, 6, 7, 3: 3, 4}
|
||||||
|
_ = A1{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ }
|
||||||
|
_ = A1{10 /* ERROR "index .* out of bounds" */ : 10, 10 /* ERROR "index .* out of bounds" */ : 10}
|
||||||
|
_ = A1{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
|
||||||
|
_ = A1{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4}
|
||||||
|
_ = A1{2.0}
|
||||||
|
_ = A1{2.1 /* ERROR "overflows" */ }
|
||||||
|
_ = A1{"foo" /* ERROR "cannot convert" */ }
|
||||||
|
|
||||||
|
a0 := [...]int{}
|
||||||
|
assert(len(a0) == 0)
|
||||||
|
|
||||||
|
a1 := [...]int{0, 1, 2}
|
||||||
|
assert(len(a1) == 3)
|
||||||
|
var a13 [3]int
|
||||||
|
var a14 [4]int
|
||||||
|
a13 = a1
|
||||||
|
a14 = a1 /* ERROR "cannot assign" */
|
||||||
|
|
||||||
|
a2 := [...]int{- /* ERROR "negative" */ 1: 0}
|
||||||
|
|
||||||
|
a3 := [...]int{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
|
||||||
|
assert(len(a3) == 5) // somewhat arbitrary
|
||||||
|
|
||||||
|
a4 := [...]complex128{0, 1, 2, 1<<10-2: -1i, 1i, 400: 10, 12, 14}
|
||||||
|
assert(len(a4) == 1024)
|
||||||
|
|
||||||
|
// from the spec
|
||||||
|
type Point struct { x, y float32 }
|
||||||
|
_ = [...]Point{Point{1.5, -3.5}, Point{0, 0}}
|
||||||
|
_ = [...]Point{{1.5, -3.5}, {0, 0}}
|
||||||
|
_ = [][]int{[]int{1, 2, 3}, []int{4, 5}}
|
||||||
|
_ = [][]int{{1, 2, 3}, {4, 5}}
|
||||||
|
_ = [...]*Point{&Point{1.5, -3.5}, &Point{0, 0}}
|
||||||
|
_ = [...]*Point{{1.5, -3.5}, {0, 0}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func slice_literals() {
|
||||||
|
type S0 []int
|
||||||
|
_ = S0{}
|
||||||
|
_ = S0{0, 1, 2}
|
||||||
|
_ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||||
|
_ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
_ = S0{- /* ERROR "negative" */ 1: 0}
|
||||||
|
_ = S0{8: 8, 9}
|
||||||
|
_ = S0{8: 8, 9, 10}
|
||||||
|
_ = S0{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
|
||||||
|
_ = S0{5: 5, 6, 7, 3: 3, 4}
|
||||||
|
_ = S0{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ }
|
||||||
|
_ = S0{10: 10, 10 /* ERROR "duplicate index" */ : 10}
|
||||||
|
_ = S0{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
|
||||||
|
_ = S0{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4}
|
||||||
|
_ = S0{2.0}
|
||||||
|
_ = S0{2.1 /* ERROR "overflows" */ }
|
||||||
|
_ = S0{"foo" /* ERROR "cannot convert" */ }
|
||||||
|
|
||||||
|
// indices must be resolved correctly
|
||||||
|
// (for details, see comment in go/parser/parser.go, method parseElement)
|
||||||
|
index1 := 1
|
||||||
|
_ = S0{index1: 1}
|
||||||
|
_ = S0{index2: 2}
|
||||||
|
_ = S0{index3 /* ERROR "undeclared name" */ : 3}
|
||||||
|
}
|
||||||
|
|
||||||
|
var index2 int = 2
|
||||||
|
|
||||||
|
func map_literals() {
|
||||||
|
type M0 map[string]int
|
||||||
|
type M1 map[bool]int
|
||||||
|
type M2 map[*int]int
|
||||||
|
|
||||||
|
_ = M0{}
|
||||||
|
_ = M0{1 /* ERROR "missing key" */ }
|
||||||
|
_ = M0{1 /* ERROR "cannot convert" */ : 2}
|
||||||
|
_ = M0{"foo": "bar" /* ERROR "cannot convert" */ }
|
||||||
|
_ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 }
|
||||||
|
|
||||||
|
// map keys must be resolved correctly
|
||||||
|
// (for details, see comment in go/parser/parser.go, method parseElement)
|
||||||
|
key1 := "foo"
|
||||||
|
_ = M0{key1: 1}
|
||||||
|
_ = M0{key2: 2}
|
||||||
|
_ = M0{key3 /* ERROR "undeclared name" */ : 2}
|
||||||
|
|
||||||
|
_ = M1{true: 1, false: 0}
|
||||||
|
_ = M2{nil: 0, &index2: 1}
|
||||||
|
}
|
||||||
|
|
||||||
|
var key2 string = "bar"
|
||||||
|
|
||||||
|
type I interface {
|
||||||
|
m()
|
||||||
|
}
|
||||||
|
|
||||||
|
type I2 interface {
|
||||||
|
m(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
type T1 struct{}
|
||||||
|
type T2 struct{}
|
||||||
|
|
||||||
|
func (T2) m(int) {}
|
||||||
|
|
||||||
|
func type_asserts() {
|
||||||
|
var x int
|
||||||
|
_ = x /* ERROR "not an interface" */ .(int)
|
||||||
|
|
||||||
|
var e interface{}
|
||||||
|
var ok bool
|
||||||
|
x, ok = e.(int)
|
||||||
|
|
||||||
|
var t I
|
||||||
|
_ = t /* ERROR "use of .* outside type switch" */ .(type)
|
||||||
|
_ = t.(T)
|
||||||
|
_ = t.(T1 /* ERROR "missing method m" */ )
|
||||||
|
_ = t.(T2 /* ERROR "wrong type for method m" */ )
|
||||||
|
_ = t.(I2 /* ERROR "wrong type for method m" */ )
|
||||||
|
}
|
||||||
|
|
||||||
|
func f0() {}
|
||||||
|
func f1(x int) {}
|
||||||
|
func f2(u float32, s string) {}
|
||||||
|
func fs(s []byte) {}
|
||||||
|
func fv(x ...int) {}
|
||||||
|
func fi(x ... interface{}) {}
|
||||||
|
|
||||||
|
func g0() {}
|
||||||
|
func g1() int { return 0}
|
||||||
|
func g2() (u float32, s string) { return }
|
||||||
|
func gs() []byte { return nil }
|
||||||
|
|
||||||
|
func _calls() {
|
||||||
|
var x int
|
||||||
|
var y float32
|
||||||
|
var s []int
|
||||||
|
|
||||||
|
f0()
|
||||||
|
_ = f0 /* ERROR "used as value" */ ()
|
||||||
|
f0(g0 /* ERROR "too many arguments" */ )
|
||||||
|
|
||||||
|
f1(0)
|
||||||
|
f1(x)
|
||||||
|
f1(10.0)
|
||||||
|
f1 /* ERROR "too few arguments" */ ()
|
||||||
|
f1(x, y /* ERROR "too many arguments" */ )
|
||||||
|
f1(s /* ERROR "cannot pass" */ )
|
||||||
|
f1(x ... /* ERROR "cannot use ..." */ )
|
||||||
|
f1(g0 /* ERROR "used as value" */ ())
|
||||||
|
f1(g1())
|
||||||
|
// f1(g2()) // TODO(gri) missing position in error message
|
||||||
|
|
||||||
|
f2 /* ERROR "too few arguments" */ ()
|
||||||
|
f2 /* ERROR "too few arguments" */ (3.14)
|
||||||
|
f2(3.14, "foo")
|
||||||
|
f2(x /* ERROR "cannot pass" */ , "foo")
|
||||||
|
f2(g0 /* ERROR "used as value" */ ())
|
||||||
|
f2 /* ERROR "too few arguments" */ (g1 /* ERROR "cannot pass" */ ())
|
||||||
|
f2(g2())
|
||||||
|
|
||||||
|
fs /* ERROR "too few arguments" */ ()
|
||||||
|
fs(g0 /* ERROR "used as value" */ ())
|
||||||
|
fs(g1 /* ERROR "cannot pass" */ ())
|
||||||
|
// fs(g2()) // TODO(gri) missing position in error message
|
||||||
|
fs(gs())
|
||||||
|
|
||||||
|
fv()
|
||||||
|
fv(1, 2.0, x)
|
||||||
|
fv(s /* ERROR "cannot pass" */ )
|
||||||
|
fv(s...)
|
||||||
|
fv(1, s /* ERROR "can only use ... with matching parameter" */ ...)
|
||||||
|
fv(gs /* ERROR "cannot pass" */ ())
|
||||||
|
fv(gs /* ERROR "cannot pass" */ ()...)
|
||||||
|
|
||||||
|
fi()
|
||||||
|
fi(1, 2.0, x, 3.14, "foo")
|
||||||
|
fi(g2())
|
||||||
|
fi(0, g2)
|
||||||
|
fi(0, g2 /* ERROR "2-valued expression" */ ())
|
||||||
|
}
|
293
go/types/testdata/shifts.src
vendored
Normal file
293
go/types/testdata/shifts.src
vendored
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
// Copyright 2013 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 shifts
|
||||||
|
|
||||||
|
func shifts1() {
|
||||||
|
// basics
|
||||||
|
var (
|
||||||
|
i0 int
|
||||||
|
u0 uint
|
||||||
|
|
||||||
|
v0 = 1<<0
|
||||||
|
v1 = 1<<i0 /* ERROR "must be unsigned" */
|
||||||
|
v2 = 1<<u0
|
||||||
|
v3 = 1<<"foo" /* ERROR "cannot convert" */
|
||||||
|
v4 = 1<<- /* ERROR "stupid shift" */ 1
|
||||||
|
v5 = 1<<1025 /* ERROR "stupid shift" */
|
||||||
|
v6 = 1 /* ERROR "overflows" */ <<100
|
||||||
|
|
||||||
|
v10 uint = 1 << 0
|
||||||
|
v11 uint = 1 << u0
|
||||||
|
v12 float32 = 1 /* ERROR "must be integer" */ << u0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func shifts2() {
|
||||||
|
// from the spec
|
||||||
|
var (
|
||||||
|
s uint = 33
|
||||||
|
i = 1<<s // 1 has type int
|
||||||
|
j int32 = 1<<s // 1 has type int32; j == 0
|
||||||
|
k = uint64(1<<s) // 1 has type uint64; k == 1<<33
|
||||||
|
m int = 1.0<<s // 1.0 has type int
|
||||||
|
// Disabled test below. gc and gccgo disagree: gc permits it per spec special case,
|
||||||
|
// gccgo does not (issue 4881). The spec special case seems not justified (issue 4883),
|
||||||
|
// and go/types agrees with gccgo.
|
||||||
|
// n = 1.0<<s != 0 // 1.0 has type int; n == false if ints are 32bits in size
|
||||||
|
n = 1.0 /* ERROR "must be integer" */ <<s != 0
|
||||||
|
o = 1<<s == 2<<s // 1 and 2 have type int; o == true if ints are 32bits in size
|
||||||
|
p = 1<<s == 1<<33 // illegal if ints are 32bits in size: 1 has type int, but 1<<33 overflows int
|
||||||
|
u = 1.0 /* ERROR "must be integer" */ <<s // illegal: 1.0 has type float64, cannot shift
|
||||||
|
u1 = 1.0 /* ERROR "must be integer" */ <<s != 0 // illegal: 1.0 has type float64, cannot shift
|
||||||
|
u2 = 1 /* ERROR "must be integer" */ <<s != 1.0 // illegal: 1 has type float64, cannot shift
|
||||||
|
v float32 = 1 /* ERROR "must be integer" */ <<s // illegal: 1 has type float32, cannot shift
|
||||||
|
w int64 = 1.0<<33 // 1.0<<33 is a constant shift expression
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func shifts3(a int16, b float32) {
|
||||||
|
// random tests
|
||||||
|
var (
|
||||||
|
s uint = 11
|
||||||
|
u = 1 /* ERROR "must be integer" */ <<s + 1.0
|
||||||
|
v complex128 = 1 /* ERROR "must be integer" */ << s + 1.0 /* ERROR "must be integer" */ << s + 1
|
||||||
|
)
|
||||||
|
x := 1.0 /* ERROR "must be integer" */ <<s + 1
|
||||||
|
shifts3(1.0 << s, 1 /* ERROR "must be integer" */ >> s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func shifts4() {
|
||||||
|
// shifts in comparisons w/ untyped operands
|
||||||
|
var s uint
|
||||||
|
|
||||||
|
_ = 1<<s == 1
|
||||||
|
_ = 1 /* ERROR "integer" */ <<s == 1.
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s == 1
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s == 1.
|
||||||
|
|
||||||
|
_ = 1<<s + 1 == 1
|
||||||
|
_ = 1 /* ERROR "integer" */ <<s + 1 == 1.
|
||||||
|
_ = 1 /* ERROR "integer" */ <<s + 1. == 1
|
||||||
|
_ = 1 /* ERROR "integer" */ <<s + 1. == 1.
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1 == 1
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1 == 1.
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1. == 1
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1. == 1.
|
||||||
|
|
||||||
|
_ = 1<<s == 1<<s
|
||||||
|
_ = 1 /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s
|
||||||
|
|
||||||
|
_ = 1<<s + 1<<s == 1
|
||||||
|
_ = 1 /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1.
|
||||||
|
_ = 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1
|
||||||
|
_ = 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1.
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1.
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1.
|
||||||
|
|
||||||
|
_ = 1<<s + 1<<s == 1<<s + 1<<s
|
||||||
|
_ = 1 /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
|
||||||
|
_ = 1 /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s
|
||||||
|
_ = 1 /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
|
||||||
|
_ = 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s
|
||||||
|
_ = 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
|
||||||
|
_ = 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s
|
||||||
|
_ = 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s
|
||||||
|
_ = 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
|
||||||
|
}
|
||||||
|
|
||||||
|
func shifts5() {
|
||||||
|
// shifts in comparisons w/ typed operands
|
||||||
|
var s uint
|
||||||
|
var x int
|
||||||
|
|
||||||
|
_ = 1<<s == x
|
||||||
|
_ = 1.<<s == x
|
||||||
|
_ = 1.1 /* ERROR "int" */ <<s == x
|
||||||
|
|
||||||
|
_ = 1<<s + x == 1
|
||||||
|
_ = 1<<s + x == 1.
|
||||||
|
_ = 1<<s + x == 1.1 /* ERROR "int" */
|
||||||
|
_ = 1.<<s + x == 1
|
||||||
|
_ = 1.<<s + x == 1.
|
||||||
|
_ = 1.<<s + x == 1.1 /* ERROR "int" */
|
||||||
|
_ = 1.1 /* ERROR "int" */ <<s + x == 1
|
||||||
|
_ = 1.1 /* ERROR "int" */ <<s + x == 1.
|
||||||
|
_ = 1.1 /* ERROR "int" */ <<s + x == 1.1
|
||||||
|
|
||||||
|
_ = 1<<s == x<<s
|
||||||
|
_ = 1.<<s == x<<s
|
||||||
|
_ = 1.1 /* ERROR "int" */ <<s == x<<s
|
||||||
|
}
|
||||||
|
|
||||||
|
func shifts6() {
|
||||||
|
// shifts as operands in non-arithmetic operations and as arguments
|
||||||
|
var a [10]int
|
||||||
|
var s uint
|
||||||
|
|
||||||
|
_ = a[1<<s]
|
||||||
|
_ = a[1.0]
|
||||||
|
_ = a[1.0<<s]
|
||||||
|
|
||||||
|
_ = make([]int, 1.0)
|
||||||
|
_ = make([]int, 1.0<<s)
|
||||||
|
_ = make([]int, 1.1 /* ERROR "integer" */ <<s)
|
||||||
|
|
||||||
|
_ = float32(1)
|
||||||
|
_ = float32(1<<s)
|
||||||
|
_ = float32(1.0)
|
||||||
|
_ = float32(1.0 /* ERROR "int" */ <<s)
|
||||||
|
_ = float32(1.1 /* ERROR "int" */ <<s)
|
||||||
|
|
||||||
|
var b []int
|
||||||
|
_ = append(b, 1<<s)
|
||||||
|
_ = append(b, 1.0<<s)
|
||||||
|
_ = append(b, 1.1 /* ERROR "must be integer" */ <<s)
|
||||||
|
|
||||||
|
var c []float32
|
||||||
|
_ = append(b, 1<<s)
|
||||||
|
_ = append(b, 1.0<<s) // should fail - see TODO in append code
|
||||||
|
_ = append(b, 1.1 /* ERROR "must be integer" */ <<s)
|
||||||
|
|
||||||
|
_ = complex(1.0 /* ERROR "must be integer" */ <<s, 0)
|
||||||
|
_ = complex(1.1 /* ERROR "must be integer" */ <<s, 0)
|
||||||
|
_ = complex(0, 1.0 /* ERROR "must be integer" */ <<s)
|
||||||
|
_ = complex(0, 1.1 /* ERROR "must be integer" */ <<s)
|
||||||
|
|
||||||
|
// TODO(gri) The delete below is not type-checked correctly yet.
|
||||||
|
// var m1 map[int]string
|
||||||
|
// delete(m1, 1<<s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func shifts7() {
|
||||||
|
// shifts of shifts
|
||||||
|
var s uint
|
||||||
|
var x int
|
||||||
|
_ = 1<<(1<<s)
|
||||||
|
_ = 1<<(1.<<s)
|
||||||
|
_ = 1. /* ERROR "integer" */ <<(1<<s)
|
||||||
|
_ = 1. /* ERROR "integer" */ <<(1.<<s)
|
||||||
|
|
||||||
|
x = 1<<(1<<s)
|
||||||
|
x = 1<<(1.<<s)
|
||||||
|
x = 1.<<(1<<s)
|
||||||
|
x = 1.<<(1.<<s)
|
||||||
|
|
||||||
|
_ = (1<<s)<<(1<<s)
|
||||||
|
_ = (1<<s)<<(1.<<s)
|
||||||
|
_ = ( /* ERROR "integer" */ 1.<<s)<<(1<<s)
|
||||||
|
_ = ( /* ERROR "integer" */ 1.<<s)<<(1.<<s)
|
||||||
|
|
||||||
|
x = (1<<s)<<(1<<s)
|
||||||
|
x = (1<<s)<<(1.<<s)
|
||||||
|
x = ( /* ERROR "integer" */ 1.<<s)<<(1<<s)
|
||||||
|
x = ( /* ERROR "integer" */ 1.<<s)<<(1.<<s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func shifts8() {
|
||||||
|
// shift examples from shift discussion: better error messages
|
||||||
|
var s uint
|
||||||
|
_ = 1.0 /* ERROR "shifted operand 1.0 \(type float64\) must be integer" */ <<s == 1
|
||||||
|
_ = 1.0 /* ERROR "shifted operand 1.0 \(type float64\) must be integer" */ <<s == 1.0
|
||||||
|
_ = 1 /* ERROR "shifted operand 1 \(type float64\) must be integer" */ <<s == 1.0
|
||||||
|
_ = 1 /* ERROR "shifted operand 1 \(type float64\) must be integer" */ <<s + 1.0 == 1
|
||||||
|
_ = 1 /* ERROR "shifted operand 1 \(type float64\) must be integer" */ <<s + 1.1 == 1
|
||||||
|
_ = 1 /* ERROR "shifted operand 1 \(type float64\) must be integer" */ <<s + 1 == 1.0
|
||||||
|
|
||||||
|
// additional cases
|
||||||
|
_ = complex(1.0 /* ERROR "shifted operand 1.0 \(type float64\) must be integer" */ <<s, 1)
|
||||||
|
_ = complex(1.0, 1 /* ERROR "shifted operand 1 \(type float64\) must be integer" */ <<s)
|
||||||
|
|
||||||
|
// TODO(gri): enable tests using conversions (the spec is unclear)
|
||||||
|
// _ = int(1.<<s)
|
||||||
|
// _ = int(1.1<<s)
|
||||||
|
// _ = float32(1<<s)
|
||||||
|
// _ = float32(1.<<s)
|
||||||
|
// _ = float32(1.1<<s)
|
||||||
|
// _ = complex64(1<<s)
|
||||||
|
// _ = complex64(1.<<s)
|
||||||
|
// _ = complex64(1.1<<s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func shifts9() {
|
||||||
|
// various originally failing snippets of code from the std library
|
||||||
|
// from src/pkg/compress/lzw/reader.go:90
|
||||||
|
{
|
||||||
|
var d struct {
|
||||||
|
bits uint32
|
||||||
|
width uint
|
||||||
|
}
|
||||||
|
_ = uint16(d.bits & (1<<d.width - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// from src/pkg/debug/dwarf/buf.go:116
|
||||||
|
{
|
||||||
|
var ux uint64
|
||||||
|
var bits uint
|
||||||
|
x := int64(ux)
|
||||||
|
if x&(1<<(bits-1)) != 0 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from src/pkg/encoding/asn1/asn1.go:160
|
||||||
|
{
|
||||||
|
var bytes []byte
|
||||||
|
if bytes[len(bytes)-1]&((1<<bytes[0])-1) != 0 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from src/pkg/math/big/rat.go:140
|
||||||
|
{
|
||||||
|
var exp int
|
||||||
|
var mantissa uint64
|
||||||
|
shift := uint64(-1022 - (exp - 1)) // [1..53)
|
||||||
|
_ = mantissa & (1<<shift - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// from src/pkg/net/interface.go:51
|
||||||
|
{
|
||||||
|
type Flags uint
|
||||||
|
var f Flags
|
||||||
|
var i int
|
||||||
|
if f&(1<<uint(i)) != 0 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from src/pkg/runtime/softfloat64.go:234
|
||||||
|
{
|
||||||
|
var gm uint64
|
||||||
|
var shift uint
|
||||||
|
_ = gm & (1<<shift - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// from src/pkg/strconv/atof.go:326
|
||||||
|
{
|
||||||
|
var mant uint64
|
||||||
|
var mantbits uint
|
||||||
|
if mant == 2<<mantbits {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from src/pkg/syscall/route_bsd.go:82
|
||||||
|
{
|
||||||
|
var Addrs int32
|
||||||
|
const rtaRtMask = 1
|
||||||
|
var i uint
|
||||||
|
if Addrs&rtaRtMask&(1<<i) == 0 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from src/pkg/text/scanner/scanner.go:540
|
||||||
|
{
|
||||||
|
var s struct { Whitespace uint64 }
|
||||||
|
var ch rune
|
||||||
|
for s.Whitespace&(1<<uint(ch)) != 0 {}
|
||||||
|
}
|
||||||
|
}
|
288
go/types/testdata/stmt0.src
vendored
Normal file
288
go/types/testdata/stmt0.src
vendored
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// statements
|
||||||
|
|
||||||
|
package stmt0
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
b, i, f, c, s := false, 1, 1.0, 1i, "foo"
|
||||||
|
b = i /* ERROR "cannot assign" */
|
||||||
|
i = f /* ERROR "cannot assign" */
|
||||||
|
f = c /* ERROR "cannot assign" */
|
||||||
|
c = s /* ERROR "cannot assign" */
|
||||||
|
s = b /* ERROR "cannot assign" */
|
||||||
|
|
||||||
|
v0 /* ERROR "mismatch" */, v1, v2 := 1, 2, 3, 4
|
||||||
|
|
||||||
|
b = true
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
i += "foo" /* ERROR "cannot convert.*int" */
|
||||||
|
|
||||||
|
f -= 1
|
||||||
|
f -= "foo" /* ERROR "cannot convert.*float64" */
|
||||||
|
|
||||||
|
c *= 1
|
||||||
|
c /= 0 /* ERROR "division by zero" */
|
||||||
|
|
||||||
|
s += "bar"
|
||||||
|
s += 1 /* ERROR "cannot convert.*string" */
|
||||||
|
|
||||||
|
var u64 uint64
|
||||||
|
u64 += 1<<u64
|
||||||
|
|
||||||
|
undeclared /* ERROR "undeclared" */ = 991
|
||||||
|
}
|
||||||
|
|
||||||
|
func incdecs() {
|
||||||
|
const c = 3.14
|
||||||
|
c /* ERROR "cannot assign" */ ++
|
||||||
|
s := "foo"
|
||||||
|
s /* ERROR "cannot convert" */ --
|
||||||
|
3.14 /* ERROR "cannot assign" */ ++
|
||||||
|
var (
|
||||||
|
x int
|
||||||
|
y float32
|
||||||
|
z complex128
|
||||||
|
)
|
||||||
|
x++
|
||||||
|
y--
|
||||||
|
z++
|
||||||
|
}
|
||||||
|
|
||||||
|
func sends() {
|
||||||
|
var ch chan int
|
||||||
|
var rch <-chan int
|
||||||
|
var x int
|
||||||
|
x /* ERROR "cannot send" */ <- x
|
||||||
|
rch /* ERROR "cannot send" */ <- x
|
||||||
|
ch <- "foo" /* ERROR "cannot convert" */
|
||||||
|
ch <- x
|
||||||
|
}
|
||||||
|
|
||||||
|
func selects() {
|
||||||
|
select {}
|
||||||
|
var (
|
||||||
|
ch chan int
|
||||||
|
sc chan <- bool
|
||||||
|
x int
|
||||||
|
)
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
ch <- x
|
||||||
|
case t, ok := <-ch:
|
||||||
|
x = t
|
||||||
|
case <-sc /* ERROR "cannot receive from send-only channel" */ :
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
default:
|
||||||
|
default /* ERROR "multiple defaults" */ :
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func gos() {
|
||||||
|
go 1 /* ERROR "expected function/method call" */
|
||||||
|
go gos()
|
||||||
|
var c chan int
|
||||||
|
go close(c)
|
||||||
|
go len(c) // TODO(gri) this should not be legal
|
||||||
|
}
|
||||||
|
|
||||||
|
func defers() {
|
||||||
|
defer 1 /* ERROR "expected function/method call" */
|
||||||
|
defer defers()
|
||||||
|
var c chan int
|
||||||
|
defer close(c)
|
||||||
|
defer len(c) // TODO(gri) this should not be legal
|
||||||
|
}
|
||||||
|
|
||||||
|
func switches() {
|
||||||
|
var x int
|
||||||
|
|
||||||
|
switch x {
|
||||||
|
default:
|
||||||
|
default /* ERROR "multiple defaults" */ :
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case 1 /* ERROR "cannot convert" */ :
|
||||||
|
}
|
||||||
|
|
||||||
|
switch int32(x) {
|
||||||
|
case 1, 2:
|
||||||
|
case x /* ERROR "cannot compare" */ :
|
||||||
|
}
|
||||||
|
|
||||||
|
switch x {
|
||||||
|
case 1 /* ERROR "overflows" */ << 100:
|
||||||
|
}
|
||||||
|
|
||||||
|
switch x {
|
||||||
|
case 1:
|
||||||
|
case 1 /* ERROR "duplicate case" */ :
|
||||||
|
case 2, 3, 4:
|
||||||
|
case 1 /* ERROR "duplicate case" */ :
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) duplicate 64bit values that don't fit into an int64 are not yet detected
|
||||||
|
switch uint64(x) {
|
||||||
|
case 1<<64-1:
|
||||||
|
case 1<<64-1:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type I interface {
|
||||||
|
m()
|
||||||
|
}
|
||||||
|
|
||||||
|
type I2 interface {
|
||||||
|
m(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
type T struct{}
|
||||||
|
type T1 struct{}
|
||||||
|
type T2 struct{}
|
||||||
|
|
||||||
|
func (T) m() {}
|
||||||
|
func (T2) m(int) {}
|
||||||
|
|
||||||
|
func typeswitches() {
|
||||||
|
var i int
|
||||||
|
var x interface{}
|
||||||
|
|
||||||
|
switch x.(type) {}
|
||||||
|
switch (x /* ERROR "outside type switch" */ .(type)) {}
|
||||||
|
|
||||||
|
switch x.(type) {
|
||||||
|
default:
|
||||||
|
default /* ERROR "multiple defaults" */ :
|
||||||
|
}
|
||||||
|
|
||||||
|
switch x := x.(type) {}
|
||||||
|
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int:
|
||||||
|
var y int = x
|
||||||
|
}
|
||||||
|
|
||||||
|
switch x := i /* ERROR "not an interface" */ .(type) {}
|
||||||
|
|
||||||
|
switch t := x.(type) {
|
||||||
|
case nil:
|
||||||
|
var v bool = t /* ERROR "cannot initialize" */
|
||||||
|
case int:
|
||||||
|
var v int = t
|
||||||
|
case float32, complex64:
|
||||||
|
var v float32 = t /* ERROR "cannot initialize" */
|
||||||
|
default:
|
||||||
|
var v float32 = t /* ERROR "cannot initialize" */
|
||||||
|
}
|
||||||
|
|
||||||
|
var t I
|
||||||
|
switch t.(type) {
|
||||||
|
case T:
|
||||||
|
case T1 /* ERROR "missing method m" */ :
|
||||||
|
case T2 /* ERROR "wrong type for method m" */ :
|
||||||
|
case I2 /* ERROR "wrong type for method m" */ :
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeswitch0() {
|
||||||
|
switch y := interface{}(nil).(type) {
|
||||||
|
case int:
|
||||||
|
// TODO(gri) y has the wrong type here (type-checking
|
||||||
|
// of captured variable is delayed)
|
||||||
|
// func() int { return y + 0 }()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rangeloops() {
|
||||||
|
var (
|
||||||
|
x int
|
||||||
|
a [10]float32
|
||||||
|
b []string
|
||||||
|
p *[10]complex128
|
||||||
|
pp **[10]complex128
|
||||||
|
s string
|
||||||
|
m map[int]bool
|
||||||
|
c chan int
|
||||||
|
sc chan<- int
|
||||||
|
rc <-chan int
|
||||||
|
)
|
||||||
|
|
||||||
|
for _ = range x /* ERROR "cannot range over" */ {}
|
||||||
|
for i := range x /* ERROR "cannot range over" */ {}
|
||||||
|
|
||||||
|
for i := range a {
|
||||||
|
var ii int
|
||||||
|
ii = i
|
||||||
|
}
|
||||||
|
for i, x := range a {
|
||||||
|
var ii int
|
||||||
|
ii = i
|
||||||
|
var xx float64
|
||||||
|
xx = x /* ERROR "cannot assign" */
|
||||||
|
}
|
||||||
|
var ii int
|
||||||
|
var xx float32
|
||||||
|
for ii, xx := range a {}
|
||||||
|
|
||||||
|
for i := range b {
|
||||||
|
var ii int
|
||||||
|
ii = i
|
||||||
|
}
|
||||||
|
for i, x := range b {
|
||||||
|
var ii int
|
||||||
|
ii = i
|
||||||
|
var xx string
|
||||||
|
xx = x
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range s {
|
||||||
|
var ii int
|
||||||
|
ii = i
|
||||||
|
}
|
||||||
|
for i, x := range s {
|
||||||
|
var ii int
|
||||||
|
ii = i
|
||||||
|
var xx rune
|
||||||
|
xx = x
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, x := range p {
|
||||||
|
var xx complex128
|
||||||
|
xx = x
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, x := range pp /* ERROR "cannot range over" */ {}
|
||||||
|
|
||||||
|
for k := range m {
|
||||||
|
var kk int32
|
||||||
|
kk = k /* ERROR "cannot assign" */
|
||||||
|
}
|
||||||
|
for k, v := range m {
|
||||||
|
var kk int
|
||||||
|
kk = k
|
||||||
|
if v {}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, _ /* ERROR "only one iteration variable" */ = range c {}
|
||||||
|
for e := range c {
|
||||||
|
var ee int
|
||||||
|
ee = e
|
||||||
|
}
|
||||||
|
for _ = range sc /* ERROR "cannot range over send-only channel" */ {}
|
||||||
|
for _ = range rc {}
|
||||||
|
|
||||||
|
// constant strings
|
||||||
|
const cs = "foo"
|
||||||
|
for i, x := range cs {}
|
||||||
|
for i, x := range "" {
|
||||||
|
var ii int
|
||||||
|
ii = i
|
||||||
|
var xx rune
|
||||||
|
xx = x
|
||||||
|
}
|
||||||
|
}
|
164
go/types/testdata/stmt1.src
vendored
Normal file
164
go/types/testdata/stmt1.src
vendored
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
// terminating statements
|
||||||
|
|
||||||
|
package stmt1
|
||||||
|
|
||||||
|
func _() {}
|
||||||
|
|
||||||
|
func _() int {} /* ERROR "missing return" */
|
||||||
|
|
||||||
|
func _() int { panic(0) }
|
||||||
|
|
||||||
|
// block statements
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
{
|
||||||
|
}
|
||||||
|
} /* ERROR "missing return" */
|
||||||
|
|
||||||
|
// if statements
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
if x < y { return }
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
if x < y { return }
|
||||||
|
} /* ERROR "missing return" */
|
||||||
|
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
if x < y {
|
||||||
|
} else { return 1
|
||||||
|
}
|
||||||
|
} /* ERROR "missing return" */
|
||||||
|
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
if x < y { return
|
||||||
|
} else { return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for statements
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
for x < y {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} /* ERROR "missing return" */
|
||||||
|
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
for {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
for {
|
||||||
|
return
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} /* ERROR "missing return" */
|
||||||
|
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
for {
|
||||||
|
for { break }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
L: for {
|
||||||
|
for { break L }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} /* ERROR "missing return" */
|
||||||
|
|
||||||
|
// switch statements
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
switch x {
|
||||||
|
case 0: return
|
||||||
|
default: return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
switch x {
|
||||||
|
case 0: return
|
||||||
|
}
|
||||||
|
} /* ERROR "missing return" */
|
||||||
|
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
switch x {
|
||||||
|
case 0: return
|
||||||
|
case 1: break
|
||||||
|
}
|
||||||
|
} /* ERROR "missing return" */
|
||||||
|
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
switch x {
|
||||||
|
case 0: return
|
||||||
|
default:
|
||||||
|
switch y {
|
||||||
|
case 0: break
|
||||||
|
}
|
||||||
|
panic(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _(x, y int) (z int) {
|
||||||
|
L: switch x {
|
||||||
|
case 0: return
|
||||||
|
default:
|
||||||
|
switch y {
|
||||||
|
case 0: break L
|
||||||
|
}
|
||||||
|
panic(0)
|
||||||
|
}
|
||||||
|
} /* ERROR "missing return" */
|
||||||
|
|
||||||
|
// select statements
|
||||||
|
func _(ch chan int) (z int) {
|
||||||
|
select {}
|
||||||
|
} // nice!
|
||||||
|
|
||||||
|
func _(ch chan int) (z int) {
|
||||||
|
select {
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
} /* ERROR "missing return" */
|
||||||
|
|
||||||
|
func _(ch chan int) (z int) {
|
||||||
|
select {
|
||||||
|
case <-ch: return
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
} /* ERROR "missing return" */
|
||||||
|
|
||||||
|
func _(ch chan int) (z int) {
|
||||||
|
select {
|
||||||
|
case <-ch: return
|
||||||
|
default:
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _(ch chan int) (z int) {
|
||||||
|
L: select {
|
||||||
|
case <-ch: return
|
||||||
|
default:
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
break L
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} /* ERROR "missing return" */
|
236
go/types/types.go
Normal file
236
go/types/types.go
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
// Copyright 2011 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 "go/ast"
|
||||||
|
|
||||||
|
// All types implement the Type interface.
|
||||||
|
type Type interface {
|
||||||
|
String() string
|
||||||
|
aType()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BasicKind describes the kind of basic type.
|
||||||
|
type BasicKind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Invalid BasicKind = iota // type is invalid
|
||||||
|
|
||||||
|
// predeclared types
|
||||||
|
Bool
|
||||||
|
Int
|
||||||
|
Int8
|
||||||
|
Int16
|
||||||
|
Int32
|
||||||
|
Int64
|
||||||
|
Uint
|
||||||
|
Uint8
|
||||||
|
Uint16
|
||||||
|
Uint32
|
||||||
|
Uint64
|
||||||
|
Uintptr
|
||||||
|
Float32
|
||||||
|
Float64
|
||||||
|
Complex64
|
||||||
|
Complex128
|
||||||
|
String
|
||||||
|
UnsafePointer
|
||||||
|
|
||||||
|
// types for untyped values
|
||||||
|
UntypedBool
|
||||||
|
UntypedInt
|
||||||
|
UntypedRune
|
||||||
|
UntypedFloat
|
||||||
|
UntypedComplex
|
||||||
|
UntypedString
|
||||||
|
UntypedNil
|
||||||
|
|
||||||
|
// aliases
|
||||||
|
Byte = Uint8
|
||||||
|
Rune = Int32
|
||||||
|
)
|
||||||
|
|
||||||
|
// BasicInfo is a set of flags describing properties of a basic type.
|
||||||
|
type BasicInfo int
|
||||||
|
|
||||||
|
// Properties of basic types.
|
||||||
|
const (
|
||||||
|
IsBoolean BasicInfo = 1 << iota
|
||||||
|
IsInteger
|
||||||
|
IsUnsigned
|
||||||
|
IsFloat
|
||||||
|
IsComplex
|
||||||
|
IsString
|
||||||
|
IsUntyped
|
||||||
|
|
||||||
|
IsOrdered = IsInteger | IsFloat | IsString
|
||||||
|
IsNumeric = IsInteger | IsFloat | IsComplex
|
||||||
|
IsConstType = IsBoolean | IsNumeric | IsString
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Basic represents a basic type.
|
||||||
|
type Basic struct {
|
||||||
|
Kind BasicKind
|
||||||
|
Info BasicInfo
|
||||||
|
size int64 // use DefaultSizeof to get size
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Array represents an array type [Len]Elt.
|
||||||
|
type Array struct {
|
||||||
|
Len int64
|
||||||
|
Elt Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Slice represents a slice type []Elt.
|
||||||
|
type Slice struct {
|
||||||
|
Elt Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// A QualifiedName is a name qualified with the package that declared the name.
|
||||||
|
// Note: Pkg may be a fake package (no name, no scope) because the GC compiler's
|
||||||
|
// export information doesn't provide full information in some cases.
|
||||||
|
// TODO(gri): Should change Pkg to PkgPath since it's the only thing we care about.
|
||||||
|
type QualifiedName struct {
|
||||||
|
Pkg *Package // nil only for predeclared error.Error (exported)
|
||||||
|
Name string // unqualified type name for anonymous fields
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSame reports whether p and q are the same.
|
||||||
|
func (p QualifiedName) IsSame(q QualifiedName) bool {
|
||||||
|
// spec:
|
||||||
|
// "Two identifiers are different if they are spelled differently,
|
||||||
|
// or if they appear in different packages and are not exported.
|
||||||
|
// Otherwise, they are the same."
|
||||||
|
if p.Name != q.Name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// p.Name == q.Name
|
||||||
|
return ast.IsExported(p.Name) || p.Pkg.Path == q.Pkg.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Field represents a field of a struct.
|
||||||
|
type Field struct {
|
||||||
|
QualifiedName
|
||||||
|
Type Type
|
||||||
|
Tag string
|
||||||
|
IsAnonymous bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Struct represents a struct type struct{...}.
|
||||||
|
type Struct struct {
|
||||||
|
Fields []*Field
|
||||||
|
offsets []int64 // field offsets in bytes, lazily computed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (typ *Struct) fieldIndex(name QualifiedName) int {
|
||||||
|
for i, f := range typ.Fields {
|
||||||
|
if f.QualifiedName.IsSame(name) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Pointer represents a pointer type *Base.
|
||||||
|
type Pointer struct {
|
||||||
|
Base Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Result represents a (multi-value) function call result.
|
||||||
|
type Result struct {
|
||||||
|
Values []*Var // Signature.Results of the function called
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Signature represents a user-defined function type func(...) (...).
|
||||||
|
type Signature struct {
|
||||||
|
Recv *Var // nil if not a method
|
||||||
|
Params []*Var // (incoming) parameters from left to right; or nil
|
||||||
|
Results []*Var // (outgoing) results from left to right; or nil
|
||||||
|
IsVariadic bool // true if the last parameter's type is of the form ...T
|
||||||
|
}
|
||||||
|
|
||||||
|
// builtinId is an id of a builtin function.
|
||||||
|
type builtinId int
|
||||||
|
|
||||||
|
// Predeclared builtin functions.
|
||||||
|
const (
|
||||||
|
// Universe scope
|
||||||
|
_Append builtinId = iota
|
||||||
|
_Cap
|
||||||
|
_Close
|
||||||
|
_Complex
|
||||||
|
_Copy
|
||||||
|
_Delete
|
||||||
|
_Imag
|
||||||
|
_Len
|
||||||
|
_Make
|
||||||
|
_New
|
||||||
|
_Panic
|
||||||
|
_Print
|
||||||
|
_Println
|
||||||
|
_Real
|
||||||
|
_Recover
|
||||||
|
|
||||||
|
// Unsafe package
|
||||||
|
_Alignof
|
||||||
|
_Offsetof
|
||||||
|
_Sizeof
|
||||||
|
|
||||||
|
// Testing support
|
||||||
|
_Assert
|
||||||
|
_Trace
|
||||||
|
)
|
||||||
|
|
||||||
|
// A builtin represents the type of a built-in function.
|
||||||
|
type builtin struct {
|
||||||
|
id builtinId
|
||||||
|
name string
|
||||||
|
nargs int // number of arguments (minimum if variadic)
|
||||||
|
isVariadic bool
|
||||||
|
isStatement bool // true if the built-in is valid as an expression statement
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Method represents a method.
|
||||||
|
type Method struct {
|
||||||
|
QualifiedName
|
||||||
|
Type *Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Interface represents an interface type interface{...}.
|
||||||
|
type Interface struct {
|
||||||
|
Methods []*Method // TODO(gri) consider keeping them in sorted order
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Map represents a map type map[Key]Elt.
|
||||||
|
type Map struct {
|
||||||
|
Key, Elt Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Chan represents a channel type chan Elt, <-chan Elt, or chan<-Elt.
|
||||||
|
type Chan struct {
|
||||||
|
Dir ast.ChanDir
|
||||||
|
Elt Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// A NamedType represents a named type as declared in a type declaration.
|
||||||
|
type NamedType struct {
|
||||||
|
Obj *TypeName // corresponding declared object
|
||||||
|
Underlying Type // nil if not fully declared yet; never a *NamedType
|
||||||
|
Methods []*Method // TODO(gri) consider keeping them in sorted order
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Basic) aType() {}
|
||||||
|
func (*Array) aType() {}
|
||||||
|
func (*Slice) aType() {}
|
||||||
|
func (*Struct) aType() {}
|
||||||
|
func (*Pointer) aType() {}
|
||||||
|
func (*Result) aType() {}
|
||||||
|
func (*Signature) aType() {}
|
||||||
|
func (*builtin) aType() {}
|
||||||
|
func (*Interface) aType() {}
|
||||||
|
func (*Map) aType() {}
|
||||||
|
func (*Chan) aType() {}
|
||||||
|
func (*NamedType) aType() {}
|
171
go/types/types_test.go
Normal file
171
go/types/types_test.go
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
// Copyright 2012 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 contains tests verifying the types associated with an AST after
|
||||||
|
// type checking.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const filename = "<src>"
|
||||||
|
|
||||||
|
func makePkg(t *testing.T, src string) (*Package, error) {
|
||||||
|
file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pkg, err := Check(fset, []*ast.File{file})
|
||||||
|
return pkg, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type testEntry struct {
|
||||||
|
src, str string
|
||||||
|
}
|
||||||
|
|
||||||
|
// dup returns a testEntry where both src and str are the same.
|
||||||
|
func dup(s string) testEntry {
|
||||||
|
return testEntry{s, s}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testTypes = []testEntry{
|
||||||
|
// basic types
|
||||||
|
dup("int"),
|
||||||
|
dup("float32"),
|
||||||
|
dup("string"),
|
||||||
|
|
||||||
|
// arrays
|
||||||
|
dup("[10]int"),
|
||||||
|
|
||||||
|
// slices
|
||||||
|
dup("[]int"),
|
||||||
|
dup("[][]int"),
|
||||||
|
|
||||||
|
// structs
|
||||||
|
dup("struct{}"),
|
||||||
|
dup("struct{x int}"),
|
||||||
|
{`struct {
|
||||||
|
x, y int
|
||||||
|
z float32 "foo"
|
||||||
|
}`, `struct{x int; y int; z float32 "foo"}`},
|
||||||
|
{`struct {
|
||||||
|
string
|
||||||
|
elems []T
|
||||||
|
}`, `struct{string; elems []T}`},
|
||||||
|
|
||||||
|
// pointers
|
||||||
|
dup("*int"),
|
||||||
|
dup("***struct{}"),
|
||||||
|
dup("*struct{a int; b float32}"),
|
||||||
|
|
||||||
|
// functions
|
||||||
|
dup("func()"),
|
||||||
|
dup("func(x int)"),
|
||||||
|
{"func(x, y int)", "func(x int, y int)"},
|
||||||
|
{"func(x, y int, z string)", "func(x int, y int, z string)"},
|
||||||
|
dup("func(int)"),
|
||||||
|
{"func(int, string, byte)", "func(int, string, byte)"},
|
||||||
|
|
||||||
|
dup("func() int"),
|
||||||
|
{"func() (string)", "func() string"},
|
||||||
|
dup("func() (u int)"),
|
||||||
|
{"func() (u, v int, w string)", "func() (u int, v int, w string)"},
|
||||||
|
|
||||||
|
dup("func(int) string"),
|
||||||
|
dup("func(x int) string"),
|
||||||
|
dup("func(x int) (u string)"),
|
||||||
|
{"func(x, y int) (u string)", "func(x int, y int) (u string)"},
|
||||||
|
|
||||||
|
dup("func(...int) string"),
|
||||||
|
dup("func(x ...int) string"),
|
||||||
|
dup("func(x ...int) (u string)"),
|
||||||
|
{"func(x, y ...int) (u string)", "func(x int, y ...int) (u string)"},
|
||||||
|
|
||||||
|
// interfaces
|
||||||
|
dup("interface{}"),
|
||||||
|
dup("interface{m()}"),
|
||||||
|
dup(`interface{m(int) float32; String() string}`),
|
||||||
|
// TODO(gri) add test for interface w/ anonymous field
|
||||||
|
|
||||||
|
// maps
|
||||||
|
dup("map[string]int"),
|
||||||
|
{"map[struct{x, y int}][]byte", "map[struct{x int; y int}][]byte"},
|
||||||
|
|
||||||
|
// channels
|
||||||
|
dup("chan int"),
|
||||||
|
dup("chan<- func()"),
|
||||||
|
dup("<-chan []func() int"),
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTypes(t *testing.T) {
|
||||||
|
for _, test := range testTypes {
|
||||||
|
src := "package p; type T " + test.src
|
||||||
|
pkg, err := makePkg(t, src)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: %s", src, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
typ := underlying(pkg.Scope.Lookup("T").GetType())
|
||||||
|
str := typeString(typ)
|
||||||
|
if str != test.str {
|
||||||
|
t.Errorf("%s: got %s, want %s", test.src, str, test.str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testExprs = []testEntry{
|
||||||
|
// basic type literals
|
||||||
|
dup("x"),
|
||||||
|
dup("true"),
|
||||||
|
dup("42"),
|
||||||
|
dup("3.1415"),
|
||||||
|
dup("2.71828i"),
|
||||||
|
dup(`'a'`),
|
||||||
|
dup(`"foo"`),
|
||||||
|
dup("`bar`"),
|
||||||
|
|
||||||
|
// arbitrary expressions
|
||||||
|
dup("&x"),
|
||||||
|
dup("*&x"),
|
||||||
|
dup("(x)"),
|
||||||
|
dup("x + y"),
|
||||||
|
dup("x + y * 10"),
|
||||||
|
dup("t.foo"),
|
||||||
|
dup("s[0]"),
|
||||||
|
dup("s[x:y]"),
|
||||||
|
dup("s[:y]"),
|
||||||
|
dup("s[x:]"),
|
||||||
|
dup("s[:]"),
|
||||||
|
dup("f(1, 2.3)"),
|
||||||
|
dup("-f(10, 20)"),
|
||||||
|
dup("f(x + y, +3.1415)"),
|
||||||
|
{"func(a, b int) {}", "(func literal)"},
|
||||||
|
{"func(a, b int) []int {}(1, 2)[x]", "(func literal)(1, 2)[x]"},
|
||||||
|
{"[]int{1, 2, 3}", "(composite literal)"},
|
||||||
|
{"[]int{1, 2, 3}[x:]", "(composite literal)[x:]"},
|
||||||
|
{"i.([]string)", "i.(...)"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExprs(t *testing.T) {
|
||||||
|
for _, test := range testExprs {
|
||||||
|
src := "package p; var _ = " + test.src + "; var (x, y int; s []string; f func(int, float32) int; i interface{}; t interface { foo() })"
|
||||||
|
file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: %s", src, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// TODO(gri) writing the code below w/o the decl variable will
|
||||||
|
// cause a 386 compiler error (out of fixed registers)
|
||||||
|
decl := file.Decls[0].(*ast.GenDecl)
|
||||||
|
expr := decl.Specs[0].(*ast.ValueSpec).Values[0]
|
||||||
|
str := exprString(expr)
|
||||||
|
if str != test.str {
|
||||||
|
t.Errorf("%s: got %s, want %s", test.src, str, test.str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
148
go/types/universe.go
Normal file
148
go/types/universe.go
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
// Copyright 2011 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 the universe and unsafe package scopes.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Universe *Scope
|
||||||
|
Unsafe *Package
|
||||||
|
universeIota *Const
|
||||||
|
)
|
||||||
|
|
||||||
|
// Predeclared types, indexed by BasicKind.
|
||||||
|
var Typ = [...]*Basic{
|
||||||
|
Invalid: {Invalid, 0, 0, "invalid type"},
|
||||||
|
|
||||||
|
Bool: {Bool, IsBoolean, 1, "bool"},
|
||||||
|
Int: {Int, IsInteger, 0, "int"},
|
||||||
|
Int8: {Int8, IsInteger, 1, "int8"},
|
||||||
|
Int16: {Int16, IsInteger, 2, "int16"},
|
||||||
|
Int32: {Int32, IsInteger, 4, "int32"},
|
||||||
|
Int64: {Int64, IsInteger, 8, "int64"},
|
||||||
|
Uint: {Uint, IsInteger | IsUnsigned, 0, "uint"},
|
||||||
|
Uint8: {Uint8, IsInteger | IsUnsigned, 1, "uint8"},
|
||||||
|
Uint16: {Uint16, IsInteger | IsUnsigned, 2, "uint16"},
|
||||||
|
Uint32: {Uint32, IsInteger | IsUnsigned, 4, "uint32"},
|
||||||
|
Uint64: {Uint64, IsInteger | IsUnsigned, 8, "uint64"},
|
||||||
|
Uintptr: {Uintptr, IsInteger | IsUnsigned, 0, "uintptr"},
|
||||||
|
Float32: {Float32, IsFloat, 4, "float32"},
|
||||||
|
Float64: {Float64, IsFloat, 8, "float64"},
|
||||||
|
Complex64: {Complex64, IsComplex, 8, "complex64"},
|
||||||
|
Complex128: {Complex128, IsComplex, 16, "complex128"},
|
||||||
|
String: {String, IsString, 0, "string"},
|
||||||
|
UnsafePointer: {UnsafePointer, 0, 0, "Pointer"},
|
||||||
|
|
||||||
|
UntypedBool: {UntypedBool, IsBoolean | IsUntyped, 0, "untyped boolean"},
|
||||||
|
UntypedInt: {UntypedInt, IsInteger | IsUntyped, 0, "untyped integer"},
|
||||||
|
UntypedRune: {UntypedRune, IsInteger | IsUntyped, 0, "untyped rune"},
|
||||||
|
UntypedFloat: {UntypedFloat, IsFloat | IsUntyped, 0, "untyped float"},
|
||||||
|
UntypedComplex: {UntypedComplex, IsComplex | IsUntyped, 0, "untyped complex"},
|
||||||
|
UntypedString: {UntypedString, IsString | IsUntyped, 0, "untyped string"},
|
||||||
|
UntypedNil: {UntypedNil, IsUntyped, 0, "untyped nil"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var aliases = [...]*Basic{
|
||||||
|
{Byte, IsInteger | IsUnsigned, 1, "byte"},
|
||||||
|
{Rune, IsInteger, 4, "rune"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var predeclaredConstants = [...]*Const{
|
||||||
|
{Name: "true", Type: Typ[UntypedBool], Val: exact.MakeBool(true)},
|
||||||
|
{Name: "false", Type: Typ[UntypedBool], Val: exact.MakeBool(false)},
|
||||||
|
{Name: "iota", Type: Typ[UntypedInt], Val: exact.MakeInt64(0)},
|
||||||
|
{Name: "nil", Type: Typ[UntypedNil], Val: exact.MakeNil()},
|
||||||
|
}
|
||||||
|
|
||||||
|
var predeclaredFunctions = [...]*builtin{
|
||||||
|
{_Append, "append", 1, true, false},
|
||||||
|
{_Cap, "cap", 1, false, false},
|
||||||
|
{_Close, "close", 1, false, true},
|
||||||
|
{_Complex, "complex", 2, false, false},
|
||||||
|
{_Copy, "copy", 2, false, true},
|
||||||
|
{_Delete, "delete", 2, false, true},
|
||||||
|
{_Imag, "imag", 1, false, false},
|
||||||
|
{_Len, "len", 1, false, false},
|
||||||
|
{_Make, "make", 1, true, false},
|
||||||
|
{_New, "new", 1, false, false},
|
||||||
|
{_Panic, "panic", 1, false, true},
|
||||||
|
{_Print, "print", 0, true, true},
|
||||||
|
{_Println, "println", 0, true, true},
|
||||||
|
{_Real, "real", 1, false, false},
|
||||||
|
{_Recover, "recover", 0, false, true},
|
||||||
|
|
||||||
|
{_Alignof, "Alignof", 1, false, false},
|
||||||
|
{_Offsetof, "Offsetof", 1, false, false},
|
||||||
|
{_Sizeof, "Sizeof", 1, false, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Universe = new(Scope)
|
||||||
|
Unsafe = &Package{Name: "unsafe", Scope: new(Scope)}
|
||||||
|
|
||||||
|
// predeclared types
|
||||||
|
for _, t := range Typ {
|
||||||
|
def(&TypeName{Name: t.Name, Type: t})
|
||||||
|
}
|
||||||
|
for _, t := range aliases {
|
||||||
|
def(&TypeName{Name: t.Name, Type: t})
|
||||||
|
}
|
||||||
|
|
||||||
|
// error type
|
||||||
|
{
|
||||||
|
// Error has a nil package in its qualified name since it is in no package
|
||||||
|
err := &Method{QualifiedName{nil, "Error"}, &Signature{Results: []*Var{{Name: "", Type: Typ[String]}}}}
|
||||||
|
def(&TypeName{Name: "error", Type: &NamedType{Underlying: &Interface{Methods: []*Method{err}}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range predeclaredConstants {
|
||||||
|
def(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range predeclaredFunctions {
|
||||||
|
def(&Func{Name: f.name, Type: f})
|
||||||
|
}
|
||||||
|
|
||||||
|
universeIota = Universe.Lookup("iota").(*Const)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Objects with names containing blanks are internal and not entered into
|
||||||
|
// a scope. Objects with exported names are inserted in the unsafe package
|
||||||
|
// scope; other objects are inserted in the universe scope.
|
||||||
|
//
|
||||||
|
func def(obj Object) {
|
||||||
|
name := obj.GetName()
|
||||||
|
if strings.Index(name, " ") >= 0 {
|
||||||
|
return // nothing to do
|
||||||
|
}
|
||||||
|
// fix Obj link for named types
|
||||||
|
if typ, ok := obj.GetType().(*NamedType); ok {
|
||||||
|
typ.Obj = obj.(*TypeName)
|
||||||
|
}
|
||||||
|
// exported identifiers go into package unsafe
|
||||||
|
scope := Universe
|
||||||
|
if ast.IsExported(name) {
|
||||||
|
scope = Unsafe.Scope
|
||||||
|
// set Pkg field
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *TypeName:
|
||||||
|
obj.Pkg = Unsafe
|
||||||
|
case *Func:
|
||||||
|
obj.Pkg = Unsafe
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if scope.Insert(obj) != nil {
|
||||||
|
panic("internal error: double declaration")
|
||||||
|
}
|
||||||
|
}
|
63
gotype/doc.go
Normal file
63
gotype/doc.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2011 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.
|
||||||
|
|
||||||
|
/*
|
||||||
|
The gotype command does syntactic and semantic analysis of Go files
|
||||||
|
and packages similar to the analysis performed by the front-end of
|
||||||
|
a Go compiler. Errors are reported if the analysis fails; otherwise
|
||||||
|
gotype is quiet (unless -v is set).
|
||||||
|
|
||||||
|
Without a list of paths, gotype processes the standard input, which must
|
||||||
|
be the source of a single package file.
|
||||||
|
|
||||||
|
Given a list of file names, each file must be a source file belonging to
|
||||||
|
the same package unless the package name is explicitly specified with the
|
||||||
|
-p flag.
|
||||||
|
|
||||||
|
Given a directory name, gotype collects all .go files in the directory
|
||||||
|
and processes them as if they were provided as an explicit list of file
|
||||||
|
names. Each directory is processed independently. Files starting with .
|
||||||
|
or not ending in .go are ignored.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
gotype [flags] [path ...]
|
||||||
|
|
||||||
|
The flags are:
|
||||||
|
-e
|
||||||
|
Print all (including spurious) errors.
|
||||||
|
-p pkgName
|
||||||
|
Process only those files in package pkgName.
|
||||||
|
-r
|
||||||
|
Recursively process subdirectories.
|
||||||
|
-v
|
||||||
|
Verbose mode.
|
||||||
|
|
||||||
|
Debugging flags:
|
||||||
|
-comments
|
||||||
|
Parse comments (ignored if -ast not set).
|
||||||
|
-ast
|
||||||
|
Print AST (disables concurrent parsing).
|
||||||
|
-trace
|
||||||
|
Print parse trace (disables concurrent parsing).
|
||||||
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
|
||||||
|
To check the files file.go, old.saved, and .ignored:
|
||||||
|
|
||||||
|
gotype file.go old.saved .ignored
|
||||||
|
|
||||||
|
To check all .go files belonging to package main in the current directory
|
||||||
|
and recursively in all subdirectories:
|
||||||
|
|
||||||
|
gotype -p main -r .
|
||||||
|
|
||||||
|
To verify the output of a pipe:
|
||||||
|
|
||||||
|
echo "package foo" | gotype
|
||||||
|
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
// BUG(gri): At the moment, only single-file scope analysis is performed.
|
206
gotype/gotype.go
Normal file
206
gotype/gotype.go
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
// Copyright 2011 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 (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/scanner"
|
||||||
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// main operation modes
|
||||||
|
pkgName = flag.String("p", "", "process only those files in package pkgName")
|
||||||
|
recursive = flag.Bool("r", false, "recursively process subdirectories")
|
||||||
|
verbose = flag.Bool("v", false, "verbose mode")
|
||||||
|
allErrors = flag.Bool("e", false, "report all errors (not just the first 10 on different lines)")
|
||||||
|
|
||||||
|
// debugging support
|
||||||
|
parseComments = flag.Bool("comments", false, "parse comments (ignored if -ast not set)")
|
||||||
|
printTrace = flag.Bool("trace", false, "print parse trace")
|
||||||
|
printAST = flag.Bool("ast", false, "print AST")
|
||||||
|
)
|
||||||
|
|
||||||
|
var errorCount int
|
||||||
|
|
||||||
|
func usage() {
|
||||||
|
fmt.Fprintf(os.Stderr, "usage: gotype [flags] [path ...]\n")
|
||||||
|
flag.PrintDefaults()
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func report(err error) {
|
||||||
|
scanner.PrintError(os.Stderr, err)
|
||||||
|
if list, ok := err.(scanner.ErrorList); ok {
|
||||||
|
errorCount += len(list)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errorCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse returns the AST for the Go source src.
|
||||||
|
// The filename is for error reporting only.
|
||||||
|
// The result is nil if there were errors or if
|
||||||
|
// the file does not belong to the -p package.
|
||||||
|
func parse(fset *token.FileSet, filename string, src []byte) *ast.File {
|
||||||
|
if *verbose {
|
||||||
|
fmt.Println(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore files with different package name
|
||||||
|
if *pkgName != "" {
|
||||||
|
file, err := parser.ParseFile(fset, filename, src, parser.PackageClauseOnly)
|
||||||
|
if err != nil {
|
||||||
|
report(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if file.Name.Name != *pkgName {
|
||||||
|
if *verbose {
|
||||||
|
fmt.Printf("\tignored (package %s)\n", file.Name.Name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse entire file
|
||||||
|
mode := parser.DeclarationErrors
|
||||||
|
if *allErrors {
|
||||||
|
mode |= parser.AllErrors
|
||||||
|
}
|
||||||
|
if *parseComments && *printAST {
|
||||||
|
mode |= parser.ParseComments
|
||||||
|
}
|
||||||
|
if *printTrace {
|
||||||
|
mode |= parser.Trace
|
||||||
|
}
|
||||||
|
file, err := parser.ParseFile(fset, filename, src, mode)
|
||||||
|
if err != nil {
|
||||||
|
report(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if *printAST {
|
||||||
|
ast.Print(fset, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseStdin(fset *token.FileSet) (files []*ast.File) {
|
||||||
|
src, err := ioutil.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
report(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const filename = "<standard input>"
|
||||||
|
if file := parse(fset, filename, src); file != nil {
|
||||||
|
files = []*ast.File{file}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFiles(fset *token.FileSet, filenames []string) (files []*ast.File) {
|
||||||
|
for _, filename := range filenames {
|
||||||
|
src, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
report(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if file := parse(fset, filename, src); file != nil {
|
||||||
|
files = append(files, file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func isGoFilename(filename string) bool {
|
||||||
|
// ignore non-Go files
|
||||||
|
return !strings.HasPrefix(filename, ".") && strings.HasSuffix(filename, ".go")
|
||||||
|
}
|
||||||
|
|
||||||
|
func processDirectory(dirname string) {
|
||||||
|
f, err := os.Open(dirname)
|
||||||
|
if err != nil {
|
||||||
|
report(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
filenames, err := f.Readdirnames(-1)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
report(err)
|
||||||
|
// continue since filenames may not be empty
|
||||||
|
}
|
||||||
|
for i, filename := range filenames {
|
||||||
|
filenames[i] = filepath.Join(dirname, filename)
|
||||||
|
}
|
||||||
|
processFiles(filenames, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func processFiles(filenames []string, allFiles bool) {
|
||||||
|
i := 0
|
||||||
|
for _, filename := range filenames {
|
||||||
|
switch info, err := os.Stat(filename); {
|
||||||
|
case err != nil:
|
||||||
|
report(err)
|
||||||
|
case info.IsDir():
|
||||||
|
if allFiles || *recursive {
|
||||||
|
processDirectory(filename)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if allFiles || isGoFilename(info.Name()) {
|
||||||
|
filenames[i] = filename
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
processPackage(fset, parseFiles(fset, filenames[0:i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func processPackage(fset *token.FileSet, files []*ast.File) {
|
||||||
|
type bailout struct{}
|
||||||
|
ctxt := types.Context{
|
||||||
|
Error: func(err error) {
|
||||||
|
if !*allErrors && errorCount >= 10 {
|
||||||
|
panic(bailout{})
|
||||||
|
}
|
||||||
|
report(err)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
switch err := recover().(type) {
|
||||||
|
case nil, bailout:
|
||||||
|
default:
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
ctxt.Check(fset, files)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Usage = usage
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if flag.NArg() == 0 {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
processPackage(fset, parseStdin(fset))
|
||||||
|
} else {
|
||||||
|
processFiles(flag.Args(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if errorCount > 0 {
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
}
|
344
ssa/interp/external.go
Normal file
344
ssa/interp/external.go
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
// Copyright 2013 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 interp
|
||||||
|
|
||||||
|
// Emulated functions that we cannot interpret because they are
|
||||||
|
// external or because they use "unsafe" or "reflect" operations.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/ssa"
|
||||||
|
)
|
||||||
|
|
||||||
|
type externalFn func(fn *ssa.Function, args []value) value
|
||||||
|
|
||||||
|
// Key strings are from Function.FullName().
|
||||||
|
// That little dot ۰ is an Arabic zero numeral (U+06F0), categories [Nd].
|
||||||
|
var externals = map[string]externalFn{
|
||||||
|
"(reflect.Value).Bool": ext۰reflect۰Value۰Bool,
|
||||||
|
"(reflect.Value).CanAddr": ext۰reflect۰Value۰CanAddr,
|
||||||
|
"(reflect.Value).CanInterface": ext۰reflect۰Value۰CanInterface,
|
||||||
|
"(reflect.Value).Elem": ext۰reflect۰Value۰Elem,
|
||||||
|
"(reflect.Value).Field": ext۰reflect۰Value۰Field,
|
||||||
|
"(reflect.Value).Index": ext۰reflect۰Value۰Index,
|
||||||
|
"(reflect.Value).Int": ext۰reflect۰Value۰Int,
|
||||||
|
"(reflect.Value).Interface": ext۰reflect۰Value۰Interface,
|
||||||
|
"(reflect.Value).IsNil": ext۰reflect۰Value۰IsNil,
|
||||||
|
"(reflect.Value).IsValid": ext۰reflect۰Value۰IsValid,
|
||||||
|
"(reflect.Value).Kind": ext۰reflect۰Value۰Kind,
|
||||||
|
"(reflect.Value).Len": ext۰reflect۰Value۰Len,
|
||||||
|
"(reflect.Value).NumField": ext۰reflect۰Value۰NumField,
|
||||||
|
"(reflect.Value).Pointer": ext۰reflect۰Value۰Pointer,
|
||||||
|
"(reflect.Value).String": ext۰reflect۰Value۰String,
|
||||||
|
"(reflect.Value).Type": ext۰reflect۰Value۰Type,
|
||||||
|
"(reflect.error).Error": ext۰reflect۰error۰Error,
|
||||||
|
"(reflect.rtype).Bits": ext۰reflect۰rtype۰Bits,
|
||||||
|
"(reflect.rtype).Elem": ext۰reflect۰rtype۰Elem,
|
||||||
|
"(reflect.rtype).Kind": ext۰reflect۰rtype۰Kind,
|
||||||
|
"(reflect.rtype).NumOut": ext۰reflect۰rtype۰NumOut,
|
||||||
|
"(reflect.rtype).Out": ext۰reflect۰rtype۰Out,
|
||||||
|
"(reflect.rtype).String": ext۰reflect۰rtype۰String,
|
||||||
|
"bytes.Equal": ext۰bytes۰Equal,
|
||||||
|
"bytes.IndexByte": ext۰bytes۰IndexByte,
|
||||||
|
"math.Float32bits": ext۰math۰Float32bits,
|
||||||
|
"math.Float32frombits": ext۰math۰Float32frombits,
|
||||||
|
"math.Float64bits": ext۰math۰Float64bits,
|
||||||
|
"math.Float64frombits": ext۰math۰Float64frombits,
|
||||||
|
"reflect.TypeOf": ext۰reflect۰TypeOf,
|
||||||
|
"reflect.ValueOf": ext۰reflect۰ValueOf,
|
||||||
|
"reflect.init": ext۰reflect۰Init,
|
||||||
|
"reflect.valueInterface": ext۰reflect۰valueInterface,
|
||||||
|
"runtime.Breakpoint": ext۰runtime۰Breakpoint,
|
||||||
|
"runtime.GC": ext۰runtime۰GC,
|
||||||
|
"runtime.GOMAXPROCS": ext۰runtime۰GOMAXPROCS,
|
||||||
|
"runtime.Gosched": ext۰runtime۰Gosched,
|
||||||
|
"runtime.ReadMemStats": ext۰runtime۰ReadMemStats,
|
||||||
|
"runtime.SetFinalizer": ext۰runtime۰SetFinalizer,
|
||||||
|
"runtime.getgoroot": ext۰runtime۰getgoroot,
|
||||||
|
"sync/atomic.AddInt32": ext۰atomic۰AddInt32,
|
||||||
|
"sync/atomic.CompareAndSwapInt32": ext۰atomic۰CompareAndSwapInt32,
|
||||||
|
"sync/atomic.LoadInt32": ext۰atomic۰LoadInt32,
|
||||||
|
"sync/atomic.LoadUint32": ext۰atomic۰LoadUint32,
|
||||||
|
"sync/atomic.StoreInt32": ext۰atomic۰StoreInt32,
|
||||||
|
"sync/atomic.StoreUint32": ext۰atomic۰StoreUint32,
|
||||||
|
"syscall.Close": ext۰syscall۰Close,
|
||||||
|
"syscall.Exit": ext۰syscall۰Exit,
|
||||||
|
"syscall.Fstat": ext۰syscall۰Fstat,
|
||||||
|
"syscall.Getpid": ext۰syscall۰Getpid,
|
||||||
|
"syscall.Getwd": ext۰syscall۰Getwd,
|
||||||
|
"syscall.Kill": ext۰syscall۰Kill,
|
||||||
|
"syscall.Lstat": ext۰syscall۰Lstat,
|
||||||
|
"syscall.Open": ext۰syscall۰Open,
|
||||||
|
"syscall.ParseDirent": ext۰syscall۰ParseDirent,
|
||||||
|
"syscall.Read": ext۰syscall۰Read,
|
||||||
|
"syscall.ReadDirent": ext۰syscall۰ReadDirent,
|
||||||
|
"syscall.Stat": ext۰syscall۰Stat,
|
||||||
|
"syscall.Write": ext۰syscall۰Write,
|
||||||
|
"time.Sleep": ext۰time۰Sleep,
|
||||||
|
"time.now": ext۰time۰now,
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapError returns an interpreted 'error' interface value for err.
|
||||||
|
func wrapError(err error) value {
|
||||||
|
if err == nil {
|
||||||
|
return iface{}
|
||||||
|
}
|
||||||
|
return iface{t: errorType, v: err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰bytes۰Equal(fn *ssa.Function, args []value) value {
|
||||||
|
// func Equal(a, b []byte) bool
|
||||||
|
a := args[0].([]value)
|
||||||
|
b := args[1].([]value)
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range a {
|
||||||
|
if a[i] != b[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰bytes۰IndexByte(fn *ssa.Function, args []value) value {
|
||||||
|
// func IndexByte(s []byte, c byte) int
|
||||||
|
s := args[0].([]value)
|
||||||
|
c := args[1].(byte)
|
||||||
|
for i, b := range s {
|
||||||
|
if b.(byte) == c {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰math۰Float64frombits(fn *ssa.Function, args []value) value {
|
||||||
|
return math.Float64frombits(args[0].(uint64))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰math۰Float64bits(fn *ssa.Function, args []value) value {
|
||||||
|
return math.Float64bits(args[0].(float64))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰math۰Float32frombits(fn *ssa.Function, args []value) value {
|
||||||
|
return math.Float32frombits(args[0].(uint32))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰math۰Float32bits(fn *ssa.Function, args []value) value {
|
||||||
|
return math.Float32bits(args[0].(float32))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰runtime۰Breakpoint(fn *ssa.Function, args []value) value {
|
||||||
|
runtime.Breakpoint()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰runtime۰getgoroot(fn *ssa.Function, args []value) value {
|
||||||
|
return os.Getenv("GOROOT")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰runtime۰GOMAXPROCS(fn *ssa.Function, args []value) value {
|
||||||
|
return runtime.GOMAXPROCS(args[0].(int))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰runtime۰GC(fn *ssa.Function, args []value) value {
|
||||||
|
runtime.GC()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰runtime۰Gosched(fn *ssa.Function, args []value) value {
|
||||||
|
runtime.Gosched()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰runtime۰ReadMemStats(fn *ssa.Function, args []value) value {
|
||||||
|
// TODO(adonovan): populate args[0].(Struct)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰atomic۰LoadUint32(fn *ssa.Function, args []value) value {
|
||||||
|
// TODO(adonovan): fix: not atomic!
|
||||||
|
return (*args[0].(*value)).(uint32)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰atomic۰StoreUint32(fn *ssa.Function, args []value) value {
|
||||||
|
// TODO(adonovan): fix: not atomic!
|
||||||
|
*args[0].(*value) = args[1].(uint32)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰atomic۰LoadInt32(fn *ssa.Function, args []value) value {
|
||||||
|
// TODO(adonovan): fix: not atomic!
|
||||||
|
return (*args[0].(*value)).(int32)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰atomic۰StoreInt32(fn *ssa.Function, args []value) value {
|
||||||
|
// TODO(adonovan): fix: not atomic!
|
||||||
|
*args[0].(*value) = args[1].(int32)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰atomic۰CompareAndSwapInt32(fn *ssa.Function, args []value) value {
|
||||||
|
// TODO(adonovan): fix: not atomic!
|
||||||
|
p := args[0].(*value)
|
||||||
|
if (*p).(int32) == args[1].(int32) {
|
||||||
|
*p = args[2].(int32)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰atomic۰AddInt32(fn *ssa.Function, args []value) value {
|
||||||
|
// TODO(adonovan): fix: not atomic!
|
||||||
|
p := args[0].(*value)
|
||||||
|
newv := (*p).(int32) + args[1].(int32)
|
||||||
|
*p = newv
|
||||||
|
return newv
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰runtime۰SetFinalizer(fn *ssa.Function, args []value) value {
|
||||||
|
return nil // ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰time۰now(fn *ssa.Function, args []value) value {
|
||||||
|
nano := time.Now().UnixNano()
|
||||||
|
return tuple{int64(nano / 1e9), int32(nano % 1e9)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰time۰Sleep(fn *ssa.Function, args []value) value {
|
||||||
|
time.Sleep(time.Duration(args[0].(int64)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰syscall۰Exit(fn *ssa.Function, args []value) value {
|
||||||
|
panic(exitPanic(args[0].(int)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰syscall۰Getwd(fn *ssa.Function, args []value) value {
|
||||||
|
s, err := syscall.Getwd()
|
||||||
|
return tuple{s, wrapError(err)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰syscall۰Getpid(fn *ssa.Function, args []value) value {
|
||||||
|
return syscall.Getpid()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The set of remaining native functions we need to implement (as needed):
|
||||||
|
|
||||||
|
// crypto/aes/cipher_asm.go:10:func hasAsm() bool
|
||||||
|
// crypto/aes/cipher_asm.go:11:func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
|
||||||
|
// crypto/aes/cipher_asm.go:12:func decryptBlockAsm(nr int, xk *uint32, dst, src *byte)
|
||||||
|
// crypto/aes/cipher_asm.go:13:func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32)
|
||||||
|
// hash/crc32/crc32_amd64.go:12:func haveSSE42() bool
|
||||||
|
// hash/crc32/crc32_amd64.go:16:func castagnoliSSE42(crc uint32, p []byte) uint32
|
||||||
|
// math/abs.go:12:func Abs(x float64) float64
|
||||||
|
// math/asin.go:19:func Asin(x float64) float64
|
||||||
|
// math/asin.go:51:func Acos(x float64) float64
|
||||||
|
// math/atan.go:95:func Atan(x float64) float64
|
||||||
|
// math/atan2.go:29:func Atan2(y, x float64) float64
|
||||||
|
// math/big/arith_decl.go:8:func mulWW(x, y Word) (z1, z0 Word)
|
||||||
|
// math/big/arith_decl.go:9:func divWW(x1, x0, y Word) (q, r Word)
|
||||||
|
// math/big/arith_decl.go:10:func addVV(z, x, y []Word) (c Word)
|
||||||
|
// math/big/arith_decl.go:11:func subVV(z, x, y []Word) (c Word)
|
||||||
|
// math/big/arith_decl.go:12:func addVW(z, x []Word, y Word) (c Word)
|
||||||
|
// math/big/arith_decl.go:13:func subVW(z, x []Word, y Word) (c Word)
|
||||||
|
// math/big/arith_decl.go:14:func shlVU(z, x []Word, s uint) (c Word)
|
||||||
|
// math/big/arith_decl.go:15:func shrVU(z, x []Word, s uint) (c Word)
|
||||||
|
// math/big/arith_decl.go:16:func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||||
|
// math/big/arith_decl.go:17:func addMulVVW(z, x []Word, y Word) (c Word)
|
||||||
|
// math/big/arith_decl.go:18:func divWVW(z []Word, xn Word, x []Word, y Word) (r Word)
|
||||||
|
// math/big/arith_decl.go:19:func bitLen(x Word) (n int)
|
||||||
|
// math/dim.go:13:func Dim(x, y float64) float64
|
||||||
|
// math/dim.go:26:func Max(x, y float64) float64
|
||||||
|
// math/dim.go:53:func Min(x, y float64) float64
|
||||||
|
// math/exp.go:14:func Exp(x float64) float64
|
||||||
|
// math/exp.go:135:func Exp2(x float64) float64
|
||||||
|
// math/expm1.go:124:func Expm1(x float64) float64
|
||||||
|
// math/floor.go:13:func Floor(x float64) float64
|
||||||
|
// math/floor.go:36:func Ceil(x float64) float64
|
||||||
|
// math/floor.go:48:func Trunc(x float64) float64
|
||||||
|
// math/frexp.go:16:func Frexp(f float64) (frac float64, exp int)
|
||||||
|
// math/hypot.go:17:func Hypot(p, q float64) float64
|
||||||
|
// math/ldexp.go:14:func Ldexp(frac float64, exp int) float64
|
||||||
|
// math/log.go:80:func Log(x float64) float64
|
||||||
|
// math/log10.go:9:func Log10(x float64) float64
|
||||||
|
// math/log10.go:17:func Log2(x float64) float64
|
||||||
|
// math/log1p.go:95:func Log1p(x float64) float64
|
||||||
|
// math/mod.go:21:func Mod(x, y float64) float64
|
||||||
|
// math/modf.go:13:func Modf(f float64) (int float64, frac float64)
|
||||||
|
// math/remainder.go:37:func Remainder(x, y float64) float64
|
||||||
|
// math/sin.go:117:func Cos(x float64) float64
|
||||||
|
// math/sin.go:174:func Sin(x float64) float64
|
||||||
|
// math/sincos.go:15:func Sincos(x float64) (sin, cos float64)
|
||||||
|
// math/sqrt.go:14:func Sqrt(x float64) float64
|
||||||
|
// math/tan.go:82:func Tan(x float64) float64
|
||||||
|
// os/file_posix.go:14:func sigpipe() // implemented in package runtime
|
||||||
|
// os/signal/signal_unix.go:15:func signal_enable(uint32)
|
||||||
|
// os/signal/signal_unix.go:16:func signal_recv() uint32
|
||||||
|
// runtime/debug.go:13:func LockOSThread()
|
||||||
|
// runtime/debug.go:17:func UnlockOSThread()
|
||||||
|
// runtime/debug.go:27:func NumCPU() int
|
||||||
|
// runtime/debug.go:30:func NumCgoCall() int64
|
||||||
|
// runtime/debug.go:33:func NumGoroutine() int
|
||||||
|
// runtime/debug.go:90:func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool)
|
||||||
|
// runtime/debug.go:114:func ThreadCreateProfile(p []StackRecord) (n int, ok bool)
|
||||||
|
// runtime/debug.go:122:func GoroutineProfile(p []StackRecord) (n int, ok bool)
|
||||||
|
// runtime/debug.go:132:func CPUProfile() []byte
|
||||||
|
// runtime/debug.go:141:func SetCPUProfileRate(hz int)
|
||||||
|
// runtime/debug.go:149:func SetBlockProfileRate(rate int)
|
||||||
|
// runtime/debug.go:166:func BlockProfile(p []BlockProfileRecord) (n int, ok bool)
|
||||||
|
// runtime/debug.go:172:func Stack(buf []byte, all bool) int
|
||||||
|
// runtime/error.go:81:func typestring(interface{}) string
|
||||||
|
// runtime/extern.go:19:func Goexit()
|
||||||
|
// runtime/extern.go:27:func Caller(skip int) (pc uintptr, file string, line int, ok bool)
|
||||||
|
// runtime/extern.go:34:func Callers(skip int, pc []uintptr) int
|
||||||
|
// runtime/extern.go:51:func FuncForPC(pc uintptr) *Func
|
||||||
|
// runtime/extern.go:68:func funcline_go(*Func, uintptr) (string, int)
|
||||||
|
// runtime/extern.go:71:func mid() uint32
|
||||||
|
// runtime/pprof/pprof.go:667:func runtime_cyclesPerSecond() int64
|
||||||
|
// runtime/race.go:16:func RaceDisable()
|
||||||
|
// runtime/race.go:19:func RaceEnable()
|
||||||
|
// runtime/race.go:21:func RaceAcquire(addr unsafe.Pointer)
|
||||||
|
// runtime/race.go:22:func RaceRelease(addr unsafe.Pointer)
|
||||||
|
// runtime/race.go:23:func RaceReleaseMerge(addr unsafe.Pointer)
|
||||||
|
// runtime/race.go:25:func RaceRead(addr unsafe.Pointer)
|
||||||
|
// runtime/race.go:26:func RaceWrite(addr unsafe.Pointer)
|
||||||
|
// runtime/race.go:28:func RaceSemacquire(s *uint32)
|
||||||
|
// runtime/race.go:29:func RaceSemrelease(s *uint32)
|
||||||
|
// sync/atomic/doc.go:49:func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
|
||||||
|
// sync/atomic/doc.go:52:func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
|
||||||
|
// sync/atomic/doc.go:55:func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
|
||||||
|
// sync/atomic/doc.go:58:func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)
|
||||||
|
// sync/atomic/doc.go:61:func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
|
||||||
|
// sync/atomic/doc.go:67:func AddUint32(addr *uint32, delta uint32) (new uint32)
|
||||||
|
// sync/atomic/doc.go:70:func AddInt64(addr *int64, delta int64) (new int64)
|
||||||
|
// sync/atomic/doc.go:73:func AddUint64(addr *uint64, delta uint64) (new uint64)
|
||||||
|
// sync/atomic/doc.go:76:func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)
|
||||||
|
// sync/atomic/doc.go:82:func LoadInt64(addr *int64) (val int64)
|
||||||
|
// sync/atomic/doc.go:88:func LoadUint64(addr *uint64) (val uint64)
|
||||||
|
// sync/atomic/doc.go:91:func LoadUintptr(addr *uintptr) (val uintptr)
|
||||||
|
// sync/atomic/doc.go:94:func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
|
||||||
|
// sync/atomic/doc.go:100:func StoreInt64(addr *int64, val int64)
|
||||||
|
// sync/atomic/doc.go:106:func StoreUint64(addr *uint64, val uint64)
|
||||||
|
// sync/atomic/doc.go:109:func StoreUintptr(addr *uintptr, val uintptr)
|
||||||
|
// sync/atomic/doc.go:112:func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)
|
||||||
|
// sync/runtime.go:12:func runtime_Semacquire(s *uint32)
|
||||||
|
// sync/runtime.go:18:func runtime_Semrelease(s *uint32)
|
||||||
|
// syscall/env_unix.go:30:func setenv_c(k, v string)
|
||||||
|
// syscall/syscall_linux_amd64.go:60:func Gettimeofday(tv *Timeval) (err error)
|
||||||
|
// syscall/syscall_linux_amd64.go:61:func Time(t *Time_t) (tt Time_t, err error)
|
||||||
|
// syscall/syscall_linux_arm.go:28:func Seek(fd int, offset int64, whence int) (newoffset int64, err error)
|
||||||
|
// time/sleep.go:25:func startTimer(*runtimeTimer)
|
||||||
|
// time/sleep.go:26:func stopTimer(*runtimeTimer) bool
|
||||||
|
// time/time.go:758:func now() (sec int64, nsec int32)
|
||||||
|
// unsafe/unsafe.go:27:func Sizeof(v ArbitraryType) uintptr
|
||||||
|
// unsafe/unsafe.go:32:func Offsetof(v ArbitraryType) uintptr
|
||||||
|
// unsafe/unsafe.go:37:func Alignof(v ArbitraryType) uintptr
|
49
ssa/interp/external_plan9.go
Normal file
49
ssa/interp/external_plan9.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2013 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 interp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/ssa"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ext۰syscall۰Close(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.Close not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰Fstat(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.Fstat not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰Kill(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.Kill not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰Lstat(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.Lstat not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰Open(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.Open not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰ParseDirent(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.ParseDirent not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰Read(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.Read not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰ReadDirent(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.ReadDirent not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰Stat(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.Stat not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰syscall۰Write(fn *ssa.Function, args []value) value {
|
||||||
|
p := args[1].([]value)
|
||||||
|
b := make([]byte, 0, len(p))
|
||||||
|
for i := range p {
|
||||||
|
b = append(b, p[i].(byte))
|
||||||
|
}
|
||||||
|
n, err := syscall.Write(args[0].(int), b)
|
||||||
|
return tuple{n, wrapError(err)}
|
||||||
|
}
|
137
ssa/interp/external_unix.go
Normal file
137
ssa/interp/external_unix.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
// +build !windows,!plan9
|
||||||
|
|
||||||
|
package interp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/ssa"
|
||||||
|
)
|
||||||
|
|
||||||
|
func valueToBytes(v value) []byte {
|
||||||
|
in := v.([]value)
|
||||||
|
b := make([]byte, len(in))
|
||||||
|
for i := range in {
|
||||||
|
b[i] = in[i].(byte)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func fillStat(st *syscall.Stat_t, stat structure) {
|
||||||
|
stat[0] = st.Dev
|
||||||
|
stat[1] = st.Ino
|
||||||
|
stat[2] = st.Nlink
|
||||||
|
stat[3] = st.Mode
|
||||||
|
stat[4] = st.Uid
|
||||||
|
stat[5] = st.Gid
|
||||||
|
|
||||||
|
stat[7] = st.Rdev
|
||||||
|
stat[8] = st.Size
|
||||||
|
stat[9] = st.Blksize
|
||||||
|
stat[10] = st.Blocks
|
||||||
|
// TODO(adonovan): fix: copy Timespecs.
|
||||||
|
// stat[11] = st.Atim
|
||||||
|
// stat[12] = st.Mtim
|
||||||
|
// stat[13] = st.Ctim
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰syscall۰Close(fn *ssa.Function, args []value) value {
|
||||||
|
// func Close(fd int) (err error)
|
||||||
|
return wrapError(syscall.Close(args[0].(int)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰syscall۰Fstat(fn *ssa.Function, args []value) value {
|
||||||
|
// func Fstat(fd int, stat *Stat_t) (err error)
|
||||||
|
fd := args[0].(int)
|
||||||
|
stat := (*args[1].(*value)).(structure)
|
||||||
|
|
||||||
|
var st syscall.Stat_t
|
||||||
|
err := syscall.Fstat(fd, &st)
|
||||||
|
fillStat(&st, stat)
|
||||||
|
return wrapError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰syscall۰ReadDirent(fn *ssa.Function, args []value) value {
|
||||||
|
// func ReadDirent(fd int, buf []byte) (n int, err error)
|
||||||
|
fd := args[0].(int)
|
||||||
|
p := args[1].([]value)
|
||||||
|
b := make([]byte, len(p))
|
||||||
|
n, err := syscall.ReadDirent(fd, b)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
p[i] = b[i]
|
||||||
|
}
|
||||||
|
return tuple{n, wrapError(err)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰syscall۰Kill(fn *ssa.Function, args []value) value {
|
||||||
|
// func Kill(pid int, sig Signal) (err error)
|
||||||
|
return wrapError(syscall.Kill(args[0].(int), syscall.Signal(args[1].(int))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰syscall۰Lstat(fn *ssa.Function, args []value) value {
|
||||||
|
// func Lstat(name string, stat *Stat_t) (err error)
|
||||||
|
name := args[0].(string)
|
||||||
|
stat := (*args[1].(*value)).(structure)
|
||||||
|
|
||||||
|
var st syscall.Stat_t
|
||||||
|
err := syscall.Lstat(name, &st)
|
||||||
|
fillStat(&st, stat)
|
||||||
|
return wrapError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰syscall۰Open(fn *ssa.Function, args []value) value {
|
||||||
|
// func Open(path string, mode int, perm uint32) (fd int, err error) {
|
||||||
|
path := args[0].(string)
|
||||||
|
mode := args[1].(int)
|
||||||
|
perm := args[2].(uint32)
|
||||||
|
fd, err := syscall.Open(path, mode, perm)
|
||||||
|
return tuple{fd, wrapError(err)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰syscall۰ParseDirent(fn *ssa.Function, args []value) value {
|
||||||
|
// func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string)
|
||||||
|
max := args[1].(int)
|
||||||
|
var names []string
|
||||||
|
for _, iname := range args[2].([]value) {
|
||||||
|
names = append(names, iname.(string))
|
||||||
|
}
|
||||||
|
consumed, count, newnames := syscall.ParseDirent(valueToBytes(args[0]), max, names)
|
||||||
|
var inewnames []value
|
||||||
|
for _, newname := range newnames {
|
||||||
|
inewnames = append(inewnames, newname)
|
||||||
|
}
|
||||||
|
return tuple{consumed, count, inewnames}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰syscall۰Read(fn *ssa.Function, args []value) value {
|
||||||
|
// func Read(fd int, p []byte) (n int, err error)
|
||||||
|
fd := args[0].(int)
|
||||||
|
p := args[1].([]value)
|
||||||
|
b := make([]byte, len(p))
|
||||||
|
n, err := syscall.Read(fd, b)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
p[i] = b[i]
|
||||||
|
}
|
||||||
|
return tuple{n, wrapError(err)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰syscall۰Stat(fn *ssa.Function, args []value) value {
|
||||||
|
// func Stat(name string, stat *Stat_t) (err error)
|
||||||
|
name := args[0].(string)
|
||||||
|
stat := (*args[1].(*value)).(structure)
|
||||||
|
|
||||||
|
var st syscall.Stat_t
|
||||||
|
err := syscall.Stat(name, &st)
|
||||||
|
fillStat(&st, stat)
|
||||||
|
return wrapError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰syscall۰Write(fn *ssa.Function, args []value) value {
|
||||||
|
// func Write(fd int, p []byte) (n int, err error)
|
||||||
|
n, err := syscall.Write(args[0].(int), valueToBytes(args[1]))
|
||||||
|
return tuple{n, wrapError(err)}
|
||||||
|
}
|
42
ssa/interp/external_windows.go
Normal file
42
ssa/interp/external_windows.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
// +build windows plan9
|
||||||
|
|
||||||
|
package interp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.google.com/p/go.tools/ssa"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ext۰syscall۰Close(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.Close not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰Fstat(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.Fstat not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰Kill(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.Kill not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰Lstat(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.Lstat not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰Open(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.Open not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰ParseDirent(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.ParseDirent not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰Read(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.Read not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰ReadDirent(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.ReadDirent not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰Stat(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.Stat not yet implemented")
|
||||||
|
}
|
||||||
|
func ext۰syscall۰Write(fn *ssa.Function, args []value) value {
|
||||||
|
panic("syscall.Write not yet implemented")
|
||||||
|
}
|
622
ssa/interp/interp.go
Normal file
622
ssa/interp/interp.go
Normal file
@ -0,0 +1,622 @@
|
|||||||
|
// Package exp/ssa/interp defines an interpreter for the SSA
|
||||||
|
// representation of Go programs.
|
||||||
|
//
|
||||||
|
// This interpreter is provided as an adjunct for testing the SSA
|
||||||
|
// construction algorithm. Its purpose is to provide a minimal
|
||||||
|
// metacircular implementation of the dynamic semantics of each SSA
|
||||||
|
// instruction. It is not, and will never be, a production-quality Go
|
||||||
|
// interpreter.
|
||||||
|
//
|
||||||
|
// The following is a partial list of Go features that are currently
|
||||||
|
// unsupported or incomplete in the interpreter.
|
||||||
|
//
|
||||||
|
// * Unsafe operations, including all uses of unsafe.Pointer, are
|
||||||
|
// impossible to support given the "boxed" value representation we
|
||||||
|
// have chosen.
|
||||||
|
//
|
||||||
|
// * The reflect package is only partially implemented.
|
||||||
|
//
|
||||||
|
// * "sync/atomic" operations are not currently atomic due to the
|
||||||
|
// "boxed" value representation: it is not possible to read, modify
|
||||||
|
// and write an interface value atomically. As a consequence, Mutexes
|
||||||
|
// are currently broken. TODO(adonovan): provide a metacircular
|
||||||
|
// implementation of Mutex avoiding the broken atomic primitives.
|
||||||
|
//
|
||||||
|
// * recover is only partially implemented. Also, the interpreter
|
||||||
|
// makes no attempt to distinguish target panics from interpreter
|
||||||
|
// crashes.
|
||||||
|
//
|
||||||
|
// * map iteration is asymptotically inefficient.
|
||||||
|
//
|
||||||
|
// * the equivalence relation for structs doesn't skip over blank
|
||||||
|
// fields.
|
||||||
|
//
|
||||||
|
// * the sizes of the int, uint and uintptr types in the target
|
||||||
|
// program are assumed to be the same as those of the interpreter
|
||||||
|
// itself.
|
||||||
|
//
|
||||||
|
// * all values occupy space, even those of types defined by the spec
|
||||||
|
// to have zero size, e.g. struct{}. This can cause asymptotic
|
||||||
|
// performance degradation.
|
||||||
|
//
|
||||||
|
// * os.Exit is implemented using panic, causing deferred functions to
|
||||||
|
// run.
|
||||||
|
package interp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/go/types"
|
||||||
|
"code.google.com/p/go.tools/ssa"
|
||||||
|
)
|
||||||
|
|
||||||
|
type status int
|
||||||
|
|
||||||
|
const (
|
||||||
|
stRunning status = iota
|
||||||
|
stComplete
|
||||||
|
stPanic
|
||||||
|
)
|
||||||
|
|
||||||
|
type continuation int
|
||||||
|
|
||||||
|
const (
|
||||||
|
kNext continuation = iota
|
||||||
|
kReturn
|
||||||
|
kJump
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mode is a bitmask of options affecting the interpreter.
|
||||||
|
type Mode uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
DisableRecover Mode = 1 << iota // Disable recover() in target programs; show interpreter crash instead.
|
||||||
|
EnableTracing // Print a trace of all instructions as they are interpreted.
|
||||||
|
)
|
||||||
|
|
||||||
|
// State shared between all interpreted goroutines.
|
||||||
|
type interpreter struct {
|
||||||
|
prog *ssa.Program // the SSA program
|
||||||
|
globals map[ssa.Value]*value // addresses of global variables (immutable)
|
||||||
|
mode Mode // interpreter options
|
||||||
|
reflectPackage *ssa.Package // the fake reflect package
|
||||||
|
errorMethods ssa.MethodSet // the method set of reflect.error, which implements the error interface.
|
||||||
|
rtypeMethods ssa.MethodSet // the method set of rtype, which implements the reflect.Type interface.
|
||||||
|
}
|
||||||
|
|
||||||
|
type frame struct {
|
||||||
|
i *interpreter
|
||||||
|
caller *frame
|
||||||
|
fn *ssa.Function
|
||||||
|
block, prevBlock *ssa.BasicBlock
|
||||||
|
env map[ssa.Value]value // dynamic values of SSA variables
|
||||||
|
locals []value
|
||||||
|
defers []func()
|
||||||
|
result value
|
||||||
|
status status
|
||||||
|
panic interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fr *frame) get(key ssa.Value) value {
|
||||||
|
switch key := key.(type) {
|
||||||
|
case nil:
|
||||||
|
// Hack; simplifies handling of optional attributes
|
||||||
|
// such as ssa.Slice.{Low,High}.
|
||||||
|
return nil
|
||||||
|
case *ssa.Function, *ssa.Builtin:
|
||||||
|
return key
|
||||||
|
case *ssa.Literal:
|
||||||
|
return literalValue(key)
|
||||||
|
case *ssa.Global:
|
||||||
|
if r, ok := fr.i.globals[key]; ok {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r, ok := fr.env[key]; ok {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("get: no value for %T: %v", key, key.Name()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fr *frame) rundefers() {
|
||||||
|
for i := range fr.defers {
|
||||||
|
if fr.i.mode&EnableTracing != 0 {
|
||||||
|
fmt.Fprintln(os.Stderr, "Invoking deferred function", i)
|
||||||
|
}
|
||||||
|
fr.defers[len(fr.defers)-1-i]()
|
||||||
|
}
|
||||||
|
fr.defers = fr.defers[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// findMethodSet returns the method set for type typ, which may be one
|
||||||
|
// of the interpreter's fake types.
|
||||||
|
func findMethodSet(i *interpreter, typ types.Type) ssa.MethodSet {
|
||||||
|
switch typ {
|
||||||
|
case rtypeType:
|
||||||
|
return i.rtypeMethods
|
||||||
|
case errorType:
|
||||||
|
return i.errorMethods
|
||||||
|
}
|
||||||
|
return i.prog.MethodSet(typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// visitInstr interprets a single ssa.Instruction within the activation
|
||||||
|
// record frame. It returns a continuation value indicating where to
|
||||||
|
// read the next instruction from.
|
||||||
|
func visitInstr(fr *frame, instr ssa.Instruction) continuation {
|
||||||
|
switch instr := instr.(type) {
|
||||||
|
case *ssa.UnOp:
|
||||||
|
fr.env[instr] = unop(instr, fr.get(instr.X))
|
||||||
|
|
||||||
|
case *ssa.BinOp:
|
||||||
|
fr.env[instr] = binop(instr.Op, fr.get(instr.X), fr.get(instr.Y))
|
||||||
|
|
||||||
|
case *ssa.Call:
|
||||||
|
fn, args := prepareCall(fr, &instr.Call)
|
||||||
|
fr.env[instr] = call(fr.i, fr, instr.Call.Pos, fn, args)
|
||||||
|
|
||||||
|
case *ssa.Conv:
|
||||||
|
fr.env[instr] = conv(instr.Type(), instr.X.Type(), fr.get(instr.X))
|
||||||
|
|
||||||
|
case *ssa.ChangeInterface:
|
||||||
|
fr.env[instr] = fr.get(instr.X) // (can't fail)
|
||||||
|
|
||||||
|
case *ssa.MakeInterface:
|
||||||
|
fr.env[instr] = iface{t: instr.X.Type(), v: fr.get(instr.X)}
|
||||||
|
|
||||||
|
case *ssa.Extract:
|
||||||
|
fr.env[instr] = fr.get(instr.Tuple).(tuple)[instr.Index]
|
||||||
|
|
||||||
|
case *ssa.Slice:
|
||||||
|
fr.env[instr] = slice(fr.get(instr.X), fr.get(instr.Low), fr.get(instr.High))
|
||||||
|
|
||||||
|
case *ssa.Ret:
|
||||||
|
switch len(instr.Results) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
fr.result = fr.get(instr.Results[0])
|
||||||
|
default:
|
||||||
|
var res []value
|
||||||
|
for _, r := range instr.Results {
|
||||||
|
res = append(res, fr.get(r))
|
||||||
|
}
|
||||||
|
fr.result = tuple(res)
|
||||||
|
}
|
||||||
|
return kReturn
|
||||||
|
|
||||||
|
case *ssa.RunDefers:
|
||||||
|
fr.rundefers()
|
||||||
|
|
||||||
|
case *ssa.Panic:
|
||||||
|
panic(targetPanic{fr.get(instr.X)})
|
||||||
|
|
||||||
|
case *ssa.Send:
|
||||||
|
fr.get(instr.Chan).(chan value) <- copyVal(fr.get(instr.X))
|
||||||
|
|
||||||
|
case *ssa.Store:
|
||||||
|
*fr.get(instr.Addr).(*value) = copyVal(fr.get(instr.Val))
|
||||||
|
|
||||||
|
case *ssa.If:
|
||||||
|
succ := 1
|
||||||
|
if fr.get(instr.Cond).(bool) {
|
||||||
|
succ = 0
|
||||||
|
}
|
||||||
|
fr.prevBlock, fr.block = fr.block, fr.block.Succs[succ]
|
||||||
|
return kJump
|
||||||
|
|
||||||
|
case *ssa.Jump:
|
||||||
|
fr.prevBlock, fr.block = fr.block, fr.block.Succs[0]
|
||||||
|
return kJump
|
||||||
|
|
||||||
|
case *ssa.Defer:
|
||||||
|
pos := instr.Call.Pos // TODO(gri): workaround for go/types bug in typeswitch+funclit.
|
||||||
|
fn, args := prepareCall(fr, &instr.Call)
|
||||||
|
fr.defers = append(fr.defers, func() { call(fr.i, fr, pos, fn, args) })
|
||||||
|
|
||||||
|
case *ssa.Go:
|
||||||
|
fn, args := prepareCall(fr, &instr.Call)
|
||||||
|
go call(fr.i, nil, instr.Call.Pos, fn, args)
|
||||||
|
|
||||||
|
case *ssa.MakeChan:
|
||||||
|
fr.env[instr] = make(chan value, asInt(fr.get(instr.Size)))
|
||||||
|
|
||||||
|
case *ssa.Alloc:
|
||||||
|
var addr *value
|
||||||
|
if instr.Heap {
|
||||||
|
// new
|
||||||
|
addr = new(value)
|
||||||
|
fr.env[instr] = addr
|
||||||
|
} else {
|
||||||
|
// local
|
||||||
|
addr = fr.env[instr].(*value)
|
||||||
|
}
|
||||||
|
*addr = zero(indirectType(instr.Type()))
|
||||||
|
|
||||||
|
case *ssa.MakeSlice:
|
||||||
|
slice := make([]value, asInt(fr.get(instr.Cap)))
|
||||||
|
tElt := underlyingType(instr.Type()).(*types.Slice).Elt
|
||||||
|
for i := range slice {
|
||||||
|
slice[i] = zero(tElt)
|
||||||
|
}
|
||||||
|
fr.env[instr] = slice[:asInt(fr.get(instr.Len))]
|
||||||
|
|
||||||
|
case *ssa.MakeMap:
|
||||||
|
reserve := 0
|
||||||
|
if instr.Reserve != nil {
|
||||||
|
reserve = asInt(fr.get(instr.Reserve))
|
||||||
|
}
|
||||||
|
fr.env[instr] = makeMap(underlyingType(instr.Type()).(*types.Map).Key, reserve)
|
||||||
|
|
||||||
|
case *ssa.Range:
|
||||||
|
fr.env[instr] = rangeIter(fr.get(instr.X), instr.X.Type())
|
||||||
|
|
||||||
|
case *ssa.Next:
|
||||||
|
fr.env[instr] = fr.get(instr.Iter).(iter).next()
|
||||||
|
|
||||||
|
case *ssa.FieldAddr:
|
||||||
|
x := fr.get(instr.X)
|
||||||
|
fr.env[instr] = &(*x.(*value)).(structure)[instr.Field]
|
||||||
|
|
||||||
|
case *ssa.Field:
|
||||||
|
fr.env[instr] = copyVal(fr.get(instr.X).(structure)[instr.Field])
|
||||||
|
|
||||||
|
case *ssa.IndexAddr:
|
||||||
|
x := fr.get(instr.X)
|
||||||
|
idx := fr.get(instr.Index)
|
||||||
|
switch x := x.(type) {
|
||||||
|
case []value:
|
||||||
|
fr.env[instr] = &x[asInt(idx)]
|
||||||
|
case *value: // *array
|
||||||
|
fr.env[instr] = &(*x).(array)[asInt(idx)]
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected x type in IndexAddr: %T", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ssa.Index:
|
||||||
|
fr.env[instr] = copyVal(fr.get(instr.X).(array)[asInt(fr.get(instr.Index))])
|
||||||
|
|
||||||
|
case *ssa.Lookup:
|
||||||
|
fr.env[instr] = lookup(instr, fr.get(instr.X), fr.get(instr.Index))
|
||||||
|
|
||||||
|
case *ssa.MapUpdate:
|
||||||
|
m := fr.get(instr.Map)
|
||||||
|
key := fr.get(instr.Key)
|
||||||
|
v := fr.get(instr.Value)
|
||||||
|
switch m := m.(type) {
|
||||||
|
case map[value]value:
|
||||||
|
m[key] = v
|
||||||
|
case *hashmap:
|
||||||
|
m.insert(key.(hashable), v)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("illegal map type: %T", m))
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ssa.TypeAssert:
|
||||||
|
fr.env[instr] = typeAssert(fr.i, instr, fr.get(instr.X).(iface))
|
||||||
|
|
||||||
|
case *ssa.MakeClosure:
|
||||||
|
var bindings []value
|
||||||
|
for _, binding := range instr.Bindings {
|
||||||
|
bindings = append(bindings, fr.get(binding))
|
||||||
|
}
|
||||||
|
fr.env[instr] = &closure{instr.Fn.(*ssa.Function), bindings}
|
||||||
|
|
||||||
|
case *ssa.Phi:
|
||||||
|
for i, pred := range instr.Block_.Preds {
|
||||||
|
if fr.prevBlock == pred {
|
||||||
|
fr.env[instr] = fr.get(instr.Edges[i])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ssa.Select:
|
||||||
|
var cases []reflect.SelectCase
|
||||||
|
if !instr.Blocking {
|
||||||
|
cases = append(cases, reflect.SelectCase{
|
||||||
|
Dir: reflect.SelectDefault,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, state := range instr.States {
|
||||||
|
var dir reflect.SelectDir
|
||||||
|
if state.Dir == ast.RECV {
|
||||||
|
dir = reflect.SelectRecv
|
||||||
|
} else {
|
||||||
|
dir = reflect.SelectSend
|
||||||
|
}
|
||||||
|
var send reflect.Value
|
||||||
|
if state.Send != nil {
|
||||||
|
send = reflect.ValueOf(fr.get(state.Send))
|
||||||
|
}
|
||||||
|
cases = append(cases, reflect.SelectCase{
|
||||||
|
Dir: dir,
|
||||||
|
Chan: reflect.ValueOf(fr.get(state.Chan)),
|
||||||
|
Send: send,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
chosen, recv, recvOk := reflect.Select(cases)
|
||||||
|
if !instr.Blocking {
|
||||||
|
chosen-- // default case should have index -1.
|
||||||
|
}
|
||||||
|
var recvV iface
|
||||||
|
if chosen != -1 {
|
||||||
|
recvV.t = underlyingType(instr.States[chosen].Chan.Type()).(*types.Chan).Elt
|
||||||
|
if recvOk {
|
||||||
|
// No need to copy since send makes an unaliased copy.
|
||||||
|
recvV.v = recv.Interface().(value)
|
||||||
|
} else {
|
||||||
|
recvV.v = zero(recvV.t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fr.env[instr] = tuple{chosen, recvV, recvOk}
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected instruction: %T", instr))
|
||||||
|
}
|
||||||
|
|
||||||
|
// if val, ok := instr.(ssa.Value); ok {
|
||||||
|
// fmt.Println(toString(fr.env[val])) // debugging
|
||||||
|
// }
|
||||||
|
|
||||||
|
return kNext
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareCall determines the function value and argument values for a
|
||||||
|
// function call in a Call, Go or Defer instruction, peforming
|
||||||
|
// interface method lookup if needed.
|
||||||
|
//
|
||||||
|
func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) {
|
||||||
|
if call.Func != nil {
|
||||||
|
// Function call.
|
||||||
|
fn = fr.get(call.Func)
|
||||||
|
} else {
|
||||||
|
// Interface method invocation.
|
||||||
|
recv := fr.get(call.Recv).(iface)
|
||||||
|
if recv.t == nil {
|
||||||
|
panic("method invoked on nil interface")
|
||||||
|
}
|
||||||
|
id := call.MethodId()
|
||||||
|
m := findMethodSet(fr.i, recv.t)[id]
|
||||||
|
if m == nil {
|
||||||
|
// Unreachable in well-typed programs.
|
||||||
|
panic(fmt.Sprintf("method set for dynamic type %v does not contain %s", recv.t, id))
|
||||||
|
}
|
||||||
|
_, aptr := recv.v.(*value) // actual pointerness
|
||||||
|
_, fptr := m.Signature.Recv.Type.(*types.Pointer) // formal pointerness
|
||||||
|
switch {
|
||||||
|
case aptr == fptr:
|
||||||
|
args = append(args, copyVal(recv.v))
|
||||||
|
case aptr:
|
||||||
|
// Calling func(T) with a *T receiver: make a copy.
|
||||||
|
args = append(args, copyVal(*recv.v.(*value)))
|
||||||
|
case fptr:
|
||||||
|
panic("illegal call of *T method with T receiver")
|
||||||
|
}
|
||||||
|
fn = m
|
||||||
|
}
|
||||||
|
for _, arg := range call.Args {
|
||||||
|
args = append(args, fr.get(arg))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// call interprets a call to a function (function, builtin or closure)
|
||||||
|
// fn with arguments args, returning its result.
|
||||||
|
// callpos is the position of the callsite.
|
||||||
|
//
|
||||||
|
func call(i *interpreter, caller *frame, callpos token.Pos, fn value, args []value) value {
|
||||||
|
switch fn := fn.(type) {
|
||||||
|
case *ssa.Function:
|
||||||
|
if fn == nil {
|
||||||
|
panic("call of nil function") // nil of func type
|
||||||
|
}
|
||||||
|
return callSSA(i, caller, callpos, fn, args, nil)
|
||||||
|
case *closure:
|
||||||
|
return callSSA(i, caller, callpos, fn.Fn, args, fn.Env)
|
||||||
|
case *ssa.Builtin:
|
||||||
|
return callBuiltin(caller, callpos, fn, args)
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("cannot call %T", fn))
|
||||||
|
}
|
||||||
|
|
||||||
|
func loc(fset *token.FileSet, pos token.Pos) string {
|
||||||
|
if pos == token.NoPos {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return " at " + fset.Position(pos).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// callSSA interprets a call to function fn with arguments args,
|
||||||
|
// and lexical environment env, returning its result.
|
||||||
|
// callpos is the position of the callsite.
|
||||||
|
//
|
||||||
|
func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function, args []value, env []value) value {
|
||||||
|
if i.mode&EnableTracing != 0 {
|
||||||
|
fset := fn.Prog.Files
|
||||||
|
// TODO(adonovan): fix: loc() lies for external functions.
|
||||||
|
fmt.Fprintf(os.Stderr, "Entering %s%s.\n", fn.FullName(), loc(fset, fn.Pos))
|
||||||
|
suffix := ""
|
||||||
|
if caller != nil {
|
||||||
|
suffix = ", resuming " + caller.fn.FullName() + loc(fset, callpos)
|
||||||
|
}
|
||||||
|
defer fmt.Fprintf(os.Stderr, "Leaving %s%s.\n", fn.FullName(), suffix)
|
||||||
|
}
|
||||||
|
if fn.Enclosing == nil {
|
||||||
|
name := fn.FullName()
|
||||||
|
if ext := externals[name]; ext != nil {
|
||||||
|
if i.mode&EnableTracing != 0 {
|
||||||
|
fmt.Fprintln(os.Stderr, "\t(external)")
|
||||||
|
}
|
||||||
|
return ext(fn, args)
|
||||||
|
}
|
||||||
|
if fn.Blocks == nil {
|
||||||
|
panic("no code for function: " + name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fr := &frame{
|
||||||
|
i: i,
|
||||||
|
caller: caller, // currently unused; for unwinding.
|
||||||
|
fn: fn,
|
||||||
|
env: make(map[ssa.Value]value),
|
||||||
|
block: fn.Blocks[0],
|
||||||
|
locals: make([]value, len(fn.Locals)),
|
||||||
|
}
|
||||||
|
for i, l := range fn.Locals {
|
||||||
|
fr.locals[i] = zero(indirectType(l.Type()))
|
||||||
|
fr.env[l] = &fr.locals[i]
|
||||||
|
}
|
||||||
|
for i, p := range fn.Params {
|
||||||
|
fr.env[p] = args[i]
|
||||||
|
}
|
||||||
|
for i, fv := range fn.FreeVars {
|
||||||
|
fr.env[fv] = env[i]
|
||||||
|
}
|
||||||
|
var instr ssa.Instruction
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if fr.status != stComplete {
|
||||||
|
if fr.i.mode&DisableRecover != 0 {
|
||||||
|
return // let interpreter crash
|
||||||
|
}
|
||||||
|
fr.status, fr.panic = stPanic, recover()
|
||||||
|
}
|
||||||
|
fr.rundefers()
|
||||||
|
// Destroy the locals to avoid accidental use after return.
|
||||||
|
for i := range fn.Locals {
|
||||||
|
fr.locals[i] = bad{}
|
||||||
|
}
|
||||||
|
if fr.status == stPanic {
|
||||||
|
panic(fr.panic) // panic stack is not entirely clean
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
if i.mode&EnableTracing != 0 {
|
||||||
|
fmt.Fprintf(os.Stderr, ".%s:\n", fr.block)
|
||||||
|
}
|
||||||
|
block:
|
||||||
|
for _, instr = range fr.block.Instrs {
|
||||||
|
if i.mode&EnableTracing != 0 {
|
||||||
|
if v, ok := instr.(ssa.Value); ok {
|
||||||
|
fmt.Fprintln(os.Stderr, "\t", v.Name(), "=", instr)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(os.Stderr, "\t", instr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch visitInstr(fr, instr) {
|
||||||
|
case kReturn:
|
||||||
|
fr.status = stComplete
|
||||||
|
return fr.result
|
||||||
|
case kNext:
|
||||||
|
// no-op
|
||||||
|
case kJump:
|
||||||
|
break block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
// setGlobal sets the value of a system-initialized global variable.
|
||||||
|
func setGlobal(i *interpreter, pkg *ssa.Package, name string, v value) {
|
||||||
|
if g, ok := i.globals[pkg.Var(name)]; ok {
|
||||||
|
*g = v
|
||||||
|
return
|
||||||
|
}
|
||||||
|
panic("no global variable: " + pkg.Name() + "." + name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interpret interprets the Go program whose main package is mainpkg.
|
||||||
|
// mode specifies various interpreter options. filename and args are
|
||||||
|
// the initial values of os.Args for the target program.
|
||||||
|
//
|
||||||
|
// Interpret returns the exit code of the program: 2 for panic (like
|
||||||
|
// gc does), or the argument to os.Exit for normal termination.
|
||||||
|
//
|
||||||
|
func Interpret(mainpkg *ssa.Package, mode Mode, filename string, args []string) (exitCode int) {
|
||||||
|
i := &interpreter{
|
||||||
|
prog: mainpkg.Prog,
|
||||||
|
globals: make(map[ssa.Value]*value),
|
||||||
|
mode: mode,
|
||||||
|
}
|
||||||
|
initReflect(i)
|
||||||
|
|
||||||
|
for importPath, pkg := range i.prog.Packages {
|
||||||
|
// Initialize global storage.
|
||||||
|
for _, m := range pkg.Members {
|
||||||
|
switch v := m.(type) {
|
||||||
|
case *ssa.Global:
|
||||||
|
cell := zero(indirectType(v.Type()))
|
||||||
|
i.globals[v] = &cell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ad-hoc initialization for magic system variables.
|
||||||
|
switch importPath {
|
||||||
|
case "syscall":
|
||||||
|
var envs []value
|
||||||
|
for _, s := range os.Environ() {
|
||||||
|
envs = append(envs, s)
|
||||||
|
}
|
||||||
|
envs = append(envs, "GOSSAINTERP=1")
|
||||||
|
setGlobal(i, pkg, "envs", envs)
|
||||||
|
|
||||||
|
case "runtime":
|
||||||
|
// TODO(gri): expose go/types.sizeof so we can
|
||||||
|
// avoid this fragile magic number;
|
||||||
|
// unsafe.Sizeof(memStats) won't work since gc
|
||||||
|
// and go/types have different sizeof
|
||||||
|
// functions.
|
||||||
|
setGlobal(i, pkg, "sizeof_C_MStats", uintptr(3696))
|
||||||
|
|
||||||
|
case "os":
|
||||||
|
Args := []value{filename}
|
||||||
|
for _, s := range args {
|
||||||
|
Args = append(Args, s)
|
||||||
|
}
|
||||||
|
setGlobal(i, pkg, "Args", Args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top-level error handler.
|
||||||
|
exitCode = 2
|
||||||
|
defer func() {
|
||||||
|
if exitCode != 2 || i.mode&DisableRecover != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch p := recover().(type) {
|
||||||
|
case exitPanic:
|
||||||
|
exitCode = int(p)
|
||||||
|
return
|
||||||
|
case targetPanic:
|
||||||
|
fmt.Fprintln(os.Stderr, "panic:", toString(p.v))
|
||||||
|
case runtime.Error:
|
||||||
|
fmt.Fprintln(os.Stderr, "panic:", p.Error())
|
||||||
|
case string:
|
||||||
|
fmt.Fprintln(os.Stderr, "panic:", p)
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "panic: unexpected type: %T\n", p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(adonovan): dump panicking interpreter goroutine?
|
||||||
|
// buf := make([]byte, 0x10000)
|
||||||
|
// runtime.Stack(buf, false)
|
||||||
|
// fmt.Fprintln(os.Stderr, string(buf))
|
||||||
|
// (Or dump panicking target goroutine?)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Run!
|
||||||
|
call(i, nil, token.NoPos, mainpkg.Init, nil)
|
||||||
|
if mainFn := mainpkg.Func("main"); mainFn != nil {
|
||||||
|
call(i, nil, token.NoPos, mainFn, nil)
|
||||||
|
exitCode = 0
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(os.Stderr, "No main function.")
|
||||||
|
exitCode = 1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
208
ssa/interp/interp_test.go
Normal file
208
ssa/interp/interp_test.go
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
// +build !windows,!plan9
|
||||||
|
|
||||||
|
package interp_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/build"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/ssa"
|
||||||
|
"code.google.com/p/go.tools/ssa/interp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Each line contains a space-separated list of $GOROOT/test/
|
||||||
|
// filenames comprising the main package of a program.
|
||||||
|
// They are ordered quickest-first, roughly.
|
||||||
|
//
|
||||||
|
// TODO(adonovan): integrate into the $GOROOT/test driver scripts,
|
||||||
|
// golden file checking, etc.
|
||||||
|
var gorootTests = []string{
|
||||||
|
"235.go",
|
||||||
|
"alias1.go",
|
||||||
|
"chancap.go",
|
||||||
|
"func5.go",
|
||||||
|
"func6.go",
|
||||||
|
"func7.go",
|
||||||
|
"func8.go",
|
||||||
|
"helloworld.go",
|
||||||
|
"varinit.go",
|
||||||
|
"escape3.go",
|
||||||
|
"initcomma.go",
|
||||||
|
"compos.go",
|
||||||
|
"turing.go",
|
||||||
|
"indirect.go",
|
||||||
|
"complit.go",
|
||||||
|
"for.go",
|
||||||
|
"struct0.go",
|
||||||
|
"intcvt.go",
|
||||||
|
"printbig.go",
|
||||||
|
"deferprint.go",
|
||||||
|
"escape.go",
|
||||||
|
"range.go",
|
||||||
|
"const4.go",
|
||||||
|
"float_lit.go",
|
||||||
|
"bigalg.go",
|
||||||
|
"decl.go",
|
||||||
|
"if.go",
|
||||||
|
"named.go",
|
||||||
|
"bigmap.go",
|
||||||
|
"func.go",
|
||||||
|
"reorder2.go",
|
||||||
|
"closure.go",
|
||||||
|
"gc.go",
|
||||||
|
"simassign.go",
|
||||||
|
"iota.go",
|
||||||
|
"goprint.go", // doesn't actually assert anything
|
||||||
|
"utf.go",
|
||||||
|
"method.go",
|
||||||
|
"char_lit.go",
|
||||||
|
"env.go",
|
||||||
|
"int_lit.go",
|
||||||
|
"string_lit.go",
|
||||||
|
"defer.go",
|
||||||
|
"typeswitch.go",
|
||||||
|
"stringrange.go",
|
||||||
|
"reorder.go",
|
||||||
|
"literal.go",
|
||||||
|
"nul1.go",
|
||||||
|
"zerodivide.go",
|
||||||
|
"convert.go",
|
||||||
|
"convT2X.go",
|
||||||
|
"initialize.go",
|
||||||
|
"ddd.go",
|
||||||
|
"blank.go", // partly disabled; TODO(adonovan): skip blank fields in struct{_} equivalence.
|
||||||
|
"map.go",
|
||||||
|
"bom.go",
|
||||||
|
"closedchan.go",
|
||||||
|
"divide.go",
|
||||||
|
"rename.go",
|
||||||
|
"const3.go",
|
||||||
|
"nil.go",
|
||||||
|
"recover.go", // partly disabled; TODO(adonovan): fix.
|
||||||
|
// Slow tests follow.
|
||||||
|
"cmplxdivide.go cmplxdivide1.go",
|
||||||
|
"append.go",
|
||||||
|
"crlf.go", // doesn't actually assert anything
|
||||||
|
"typeswitch1.go",
|
||||||
|
"floatcmp.go",
|
||||||
|
"gc1.go",
|
||||||
|
|
||||||
|
// Working, but not worth enabling:
|
||||||
|
// "gc2.go", // works, but slow, and cheats on the memory check.
|
||||||
|
// "sigchld.go", // works, but only on POSIX.
|
||||||
|
// "peano.go", // works only up to n=9, and slow even then.
|
||||||
|
// "stack.go", // works, but too slow (~30s) by default.
|
||||||
|
// "solitaire.go", // works, but too slow (~30s).
|
||||||
|
// "const.go", // works but for but one bug: constant folder doesn't consider representations.
|
||||||
|
// "init1.go", // too slow (80s) and not that interesting. Cheats on ReadMemStats check too.
|
||||||
|
|
||||||
|
// Typechecker failures:
|
||||||
|
// "switch.go", // bug re: switch ... { case 1.0:... case 1:... }
|
||||||
|
// "rune.go", // error re: rune as index
|
||||||
|
// "64bit.go", // error re: comparison
|
||||||
|
// "cmp.go", // error re: comparison
|
||||||
|
// "rotate.go rotate0.go", // error re: shifts
|
||||||
|
// "rotate.go rotate1.go", // error re: shifts
|
||||||
|
// "rotate.go rotate2.go", // error re: shifts
|
||||||
|
// "rotate.go rotate3.go", // error re: shifts
|
||||||
|
// "run.go", // produces wrong constant for bufio.runeError; also, not really a test.
|
||||||
|
|
||||||
|
// Broken. TODO(adonovan): fix.
|
||||||
|
// copy.go // very slow; but with N=4 quickly crashes, slice index out of range.
|
||||||
|
// nilptr.go // interp: V > uintptr not implemented. Slow test, lots of mem
|
||||||
|
// recover1.go // error: "spurious recover"
|
||||||
|
// recover2.go // panic: interface conversion: string is not error: missing method Error
|
||||||
|
// recover3.go // logic errors: panicked with wrong Error.
|
||||||
|
// method3.go // Fails dynamically; (*T).f vs (T).f are distinct methods.
|
||||||
|
// args.go // works, but requires specific os.Args from the driver.
|
||||||
|
// index.go // a template, not a real test.
|
||||||
|
// mallocfin.go // SetFinalizer not implemented.
|
||||||
|
|
||||||
|
// TODO(adonovan): add tests from $GOROOT/test/* subtrees:
|
||||||
|
// bench chan bugs fixedbugs interface ken.
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are files in go.exp/ssa/interp/testdata/.
|
||||||
|
var testdataTests = []string{
|
||||||
|
"coverage.go",
|
||||||
|
"mrvchain.go",
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(t *testing.T, dir, input string) bool {
|
||||||
|
fmt.Printf("Input: %s\n", input)
|
||||||
|
|
||||||
|
var inputs []string
|
||||||
|
for _, i := range strings.Split(input, " ") {
|
||||||
|
inputs = append(inputs, dir+i)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := ssa.NewBuilder(&ssa.Context{Mode: ssa.SanityCheckFunctions, Loader: ssa.GorootLoader})
|
||||||
|
files, err := ssa.ParseFiles(b.Prog.Files, ".", inputs...)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ssa.ParseFiles(%s) failed: %s", inputs, err.Error())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print a helpful hint if we don't make it to the end.
|
||||||
|
var hint string
|
||||||
|
defer func() {
|
||||||
|
if hint != "" {
|
||||||
|
fmt.Println("FAIL")
|
||||||
|
fmt.Println(hint)
|
||||||
|
} else {
|
||||||
|
fmt.Println("PASS")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
hint = fmt.Sprintf("To dump SSA representation, run:\n%% go run exp/ssa/ssadump.go -build=CFP %s\n", input)
|
||||||
|
mainpkg, err := b.CreatePackage("main", files)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ssa.Builder.CreatePackage(%s) failed: %s", inputs, err.Error())
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
b.BuildAllPackages()
|
||||||
|
b = nil // discard Builder
|
||||||
|
|
||||||
|
hint = fmt.Sprintf("To trace execution, run:\n%% go run exp/ssa/ssadump.go -build=C -run --interp=T %s\n", input)
|
||||||
|
if exitCode := interp.Interpret(mainpkg, 0, inputs[0], []string{}); exitCode != 0 {
|
||||||
|
t.Errorf("interp.Interpret(%s) exited with code %d, want zero", inputs, exitCode)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
hint = "" // call off the hounds
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const slash = string(os.PathSeparator)
|
||||||
|
|
||||||
|
// TestInterp runs the interpreter on a selection of small Go programs.
|
||||||
|
func TestInterp(t *testing.T) {
|
||||||
|
var failures []string
|
||||||
|
|
||||||
|
for _, input := range testdataTests {
|
||||||
|
if !run(t, "testdata"+slash, input) {
|
||||||
|
failures = append(failures, input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testing.Short() {
|
||||||
|
for _, input := range gorootTests {
|
||||||
|
if !run(t, filepath.Join(build.Default.GOROOT, "test")+slash, input) {
|
||||||
|
failures = append(failures, input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if failures != nil {
|
||||||
|
fmt.Println("The following tests failed:")
|
||||||
|
for _, f := range failures {
|
||||||
|
fmt.Printf("\t%s\n", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
101
ssa/interp/map.go
Normal file
101
ssa/interp/map.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package interp
|
||||||
|
|
||||||
|
// Custom hashtable atop map.
|
||||||
|
// For use when the key's equivalence relation is not consistent with ==.
|
||||||
|
|
||||||
|
// The Go specification doesn't address the atomicity of map operations.
|
||||||
|
// The FAQ states that an implementation is permitted to crash on
|
||||||
|
// concurrent map access.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.google.com/p/go.tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type hashable interface {
|
||||||
|
hash() int
|
||||||
|
eq(x interface{}) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type entry struct {
|
||||||
|
key hashable
|
||||||
|
value value
|
||||||
|
next *entry
|
||||||
|
}
|
||||||
|
|
||||||
|
// A hashtable atop the built-in map. Since each bucket contains
|
||||||
|
// exactly one hash value, there's no need to perform hash-equality
|
||||||
|
// tests when walking the linked list. Rehashing is done by the
|
||||||
|
// underlying map.
|
||||||
|
type hashmap struct {
|
||||||
|
table map[int]*entry
|
||||||
|
length int // number of entries in map
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeMap returns an empty initialized map of key type kt,
|
||||||
|
// preallocating space for reserve elements.
|
||||||
|
func makeMap(kt types.Type, reserve int) value {
|
||||||
|
if usesBuiltinMap(kt) {
|
||||||
|
return make(map[value]value, reserve)
|
||||||
|
}
|
||||||
|
return &hashmap{table: make(map[int]*entry, reserve)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete removes the association for key k, if any.
|
||||||
|
func (m *hashmap) delete(k hashable) {
|
||||||
|
hash := k.hash()
|
||||||
|
head := m.table[hash]
|
||||||
|
if head != nil {
|
||||||
|
if k.eq(head.key) {
|
||||||
|
m.table[hash] = head.next
|
||||||
|
m.length--
|
||||||
|
return
|
||||||
|
}
|
||||||
|
prev := head
|
||||||
|
for e := head.next; e != nil; e = e.next {
|
||||||
|
if k.eq(e.key) {
|
||||||
|
prev.next = e.next
|
||||||
|
m.length--
|
||||||
|
return
|
||||||
|
}
|
||||||
|
prev = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookup returns the value associated with key k, if present, or
|
||||||
|
// value(nil) otherwise.
|
||||||
|
func (m *hashmap) lookup(k hashable) value {
|
||||||
|
hash := k.hash()
|
||||||
|
for e := m.table[hash]; e != nil; e = e.next {
|
||||||
|
if k.eq(e.key) {
|
||||||
|
return e.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert updates the map to associate key k with value v. If there
|
||||||
|
// was already an association for an eq() (though not necessarily ==)
|
||||||
|
// k, the previous key remains in the map and its associated value is
|
||||||
|
// updated.
|
||||||
|
func (m *hashmap) insert(k hashable, v value) {
|
||||||
|
hash := k.hash()
|
||||||
|
head := m.table[hash]
|
||||||
|
for e := head; e != nil; e = e.next {
|
||||||
|
if k.eq(e.key) {
|
||||||
|
e.value = v
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.table[hash] = &entry{
|
||||||
|
key: k,
|
||||||
|
value: v,
|
||||||
|
next: head,
|
||||||
|
}
|
||||||
|
m.length++
|
||||||
|
}
|
||||||
|
|
||||||
|
// len returns the number of key/value associations in the map.
|
||||||
|
func (m *hashmap) len() int {
|
||||||
|
return m.length
|
||||||
|
}
|
1375
ssa/interp/ops.go
Normal file
1375
ssa/interp/ops.go
Normal file
File diff suppressed because it is too large
Load Diff
441
ssa/interp/reflect.go
Normal file
441
ssa/interp/reflect.go
Normal file
@ -0,0 +1,441 @@
|
|||||||
|
package interp
|
||||||
|
|
||||||
|
// Emulated "reflect" package.
|
||||||
|
//
|
||||||
|
// We completely replace the built-in "reflect" package.
|
||||||
|
// The only thing clients can depend upon are that reflect.Type is an
|
||||||
|
// interface and reflect.Value is an (opaque) struct.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/go/types"
|
||||||
|
"code.google.com/p/go.tools/ssa"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A bogus "reflect" type-checker package. Shared across interpreters.
|
||||||
|
var reflectTypesPackage = &types.Package{
|
||||||
|
Name: "reflect",
|
||||||
|
Path: "reflect",
|
||||||
|
Complete: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// rtype is the concrete type the interpreter uses to implement the
|
||||||
|
// reflect.Type interface. Since its type is opaque to the target
|
||||||
|
// language, we use a types.Basic.
|
||||||
|
//
|
||||||
|
// type rtype <opaque>
|
||||||
|
var rtypeType = makeNamedType("rtype", &types.Basic{Name: "rtype"})
|
||||||
|
|
||||||
|
// error is an (interpreted) named type whose underlying type is string.
|
||||||
|
// The interpreter uses it for all implementations of the built-in error
|
||||||
|
// interface that it creates.
|
||||||
|
// We put it in the "reflect" package for expedience.
|
||||||
|
//
|
||||||
|
// type error string
|
||||||
|
var errorType = makeNamedType("error", &types.Basic{Name: "error"})
|
||||||
|
|
||||||
|
func makeNamedType(name string, underlying types.Type) *types.NamedType {
|
||||||
|
nt := &types.NamedType{Underlying: underlying}
|
||||||
|
nt.Obj = &types.TypeName{
|
||||||
|
Name: name,
|
||||||
|
Type: nt,
|
||||||
|
Pkg: reflectTypesPackage,
|
||||||
|
}
|
||||||
|
return nt
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeReflectValue(t types.Type, v value) value {
|
||||||
|
return structure{rtype{t}, v}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a reflect.Value, returns its rtype.
|
||||||
|
func rV2T(v value) rtype {
|
||||||
|
return v.(structure)[0].(rtype)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a reflect.Value, returns the underlying interpreter value.
|
||||||
|
func rV2V(v value) value {
|
||||||
|
return v.(structure)[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeReflectType boxes up an rtype in a reflect.Type interface.
|
||||||
|
func makeReflectType(rt rtype) value {
|
||||||
|
return iface{rtypeType, rt}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Init(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰rtype۰Bits(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (t reflect.rtype) int
|
||||||
|
rt := args[0].(rtype).t
|
||||||
|
basic, ok := underlyingType(rt).(*types.Basic)
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("reflect.Type.Bits(%T): non-basic type", rt))
|
||||||
|
}
|
||||||
|
switch basic.Kind {
|
||||||
|
case types.Int8, types.Uint8:
|
||||||
|
return 8
|
||||||
|
case types.Int16, types.Uint16:
|
||||||
|
return 16
|
||||||
|
case types.Int, types.UntypedInt:
|
||||||
|
// Assume sizeof(int) is same on host and target; ditto uint.
|
||||||
|
return reflect.TypeOf(int(0)).Bits()
|
||||||
|
case types.Uintptr:
|
||||||
|
// Assume sizeof(uintptr) is same on host and target.
|
||||||
|
return reflect.TypeOf(uintptr(0)).Bits()
|
||||||
|
case types.Int32, types.Uint32:
|
||||||
|
return 32
|
||||||
|
case types.Int64, types.Uint64:
|
||||||
|
return 64
|
||||||
|
case types.Float32:
|
||||||
|
return 32
|
||||||
|
case types.Float64, types.UntypedFloat:
|
||||||
|
return 64
|
||||||
|
case types.Complex64:
|
||||||
|
return 64
|
||||||
|
case types.Complex128, types.UntypedComplex:
|
||||||
|
return 128
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("reflect.Type.Bits(%s)", basic))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰rtype۰Elem(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (t reflect.rtype) reflect.Type
|
||||||
|
var elem types.Type
|
||||||
|
switch rt := underlyingType(args[0].(rtype).t).(type) {
|
||||||
|
case *types.Array:
|
||||||
|
elem = rt.Elt
|
||||||
|
case *types.Chan:
|
||||||
|
elem = rt.Elt
|
||||||
|
case *types.Map:
|
||||||
|
elem = rt.Elt
|
||||||
|
case *types.Pointer:
|
||||||
|
elem = rt.Base
|
||||||
|
case *types.Slice:
|
||||||
|
elem = rt.Elt
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("reflect.Type.Elem(%T)", rt))
|
||||||
|
}
|
||||||
|
return makeReflectType(rtype{elem})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰rtype۰Kind(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (t reflect.rtype) uint
|
||||||
|
return uint(reflectKind(args[0].(rtype).t))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰rtype۰NumOut(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (t reflect.rtype) int
|
||||||
|
return len(args[0].(rtype).t.(*types.Signature).Results)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰rtype۰Out(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (t reflect.rtype, i int) int
|
||||||
|
i := args[1].(int)
|
||||||
|
return makeReflectType(rtype{args[0].(rtype).t.(*types.Signature).Results[i].Type})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰rtype۰String(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (t reflect.rtype) string
|
||||||
|
return args[0].(rtype).t.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰TypeOf(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (t reflect.rtype) string
|
||||||
|
return makeReflectType(rtype{args[0].(iface).t})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰ValueOf(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (interface{}) reflect.Value
|
||||||
|
itf := args[0].(iface)
|
||||||
|
return makeReflectValue(itf.t, itf.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func reflectKind(t types.Type) reflect.Kind {
|
||||||
|
switch t := t.(type) {
|
||||||
|
case *types.NamedType:
|
||||||
|
return reflectKind(t.Underlying)
|
||||||
|
case *types.Basic:
|
||||||
|
switch t.Kind {
|
||||||
|
case types.Bool:
|
||||||
|
return reflect.Bool
|
||||||
|
case types.Int:
|
||||||
|
return reflect.Int
|
||||||
|
case types.Int8:
|
||||||
|
return reflect.Int8
|
||||||
|
case types.Int16:
|
||||||
|
return reflect.Int16
|
||||||
|
case types.Int32:
|
||||||
|
return reflect.Int32
|
||||||
|
case types.Int64:
|
||||||
|
return reflect.Int64
|
||||||
|
case types.Uint:
|
||||||
|
return reflect.Uint
|
||||||
|
case types.Uint8:
|
||||||
|
return reflect.Uint8
|
||||||
|
case types.Uint16:
|
||||||
|
return reflect.Uint16
|
||||||
|
case types.Uint32:
|
||||||
|
return reflect.Uint32
|
||||||
|
case types.Uint64:
|
||||||
|
return reflect.Uint64
|
||||||
|
case types.Uintptr:
|
||||||
|
return reflect.Uintptr
|
||||||
|
case types.Float32:
|
||||||
|
return reflect.Float32
|
||||||
|
case types.Float64:
|
||||||
|
return reflect.Float64
|
||||||
|
case types.Complex64:
|
||||||
|
return reflect.Complex64
|
||||||
|
case types.Complex128:
|
||||||
|
return reflect.Complex128
|
||||||
|
case types.String:
|
||||||
|
return reflect.String
|
||||||
|
case types.UnsafePointer:
|
||||||
|
return reflect.UnsafePointer
|
||||||
|
}
|
||||||
|
case *types.Array:
|
||||||
|
return reflect.Array
|
||||||
|
case *types.Chan:
|
||||||
|
return reflect.Chan
|
||||||
|
case *types.Signature:
|
||||||
|
return reflect.Func
|
||||||
|
case *types.Interface:
|
||||||
|
return reflect.Interface
|
||||||
|
case *types.Map:
|
||||||
|
return reflect.Map
|
||||||
|
case *types.Pointer:
|
||||||
|
return reflect.Ptr
|
||||||
|
case *types.Slice:
|
||||||
|
return reflect.Slice
|
||||||
|
case *types.Struct:
|
||||||
|
return reflect.Struct
|
||||||
|
}
|
||||||
|
panic(fmt.Sprint("unexpected type: ", t))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰Kind(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (reflect.Value) uint
|
||||||
|
return uint(reflectKind(rV2T(args[0]).t))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰String(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (reflect.Value) string
|
||||||
|
return toString(rV2V(args[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰Type(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (reflect.Value) reflect.Type
|
||||||
|
return makeReflectType(rV2T(args[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰Len(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (reflect.Value) int
|
||||||
|
switch v := rV2V(args[0]).(type) {
|
||||||
|
case string:
|
||||||
|
return len(v)
|
||||||
|
case array:
|
||||||
|
return len(v)
|
||||||
|
case chan value:
|
||||||
|
return cap(v)
|
||||||
|
case []value:
|
||||||
|
return len(v)
|
||||||
|
case *hashmap:
|
||||||
|
return v.len()
|
||||||
|
case map[value]value:
|
||||||
|
return len(v)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("reflect.(Value).Len(%v)", v))
|
||||||
|
}
|
||||||
|
return nil // unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰NumField(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (reflect.Value) int
|
||||||
|
return len(rV2V(args[0]).(structure))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰Pointer(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (v reflect.Value) uintptr
|
||||||
|
switch v := rV2V(args[0]).(type) {
|
||||||
|
case *value:
|
||||||
|
return uintptr(unsafe.Pointer(v))
|
||||||
|
case chan value:
|
||||||
|
return reflect.ValueOf(v).Pointer()
|
||||||
|
case []value:
|
||||||
|
return reflect.ValueOf(v).Pointer()
|
||||||
|
case *hashmap:
|
||||||
|
return reflect.ValueOf(v.table).Pointer()
|
||||||
|
case map[value]value:
|
||||||
|
return reflect.ValueOf(v).Pointer()
|
||||||
|
case *ssa.Function:
|
||||||
|
return uintptr(unsafe.Pointer(v))
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("reflect.(Value).Pointer(%T)", v))
|
||||||
|
}
|
||||||
|
return nil // unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰Index(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (v reflect.Value, i int) Value
|
||||||
|
i := args[1].(int)
|
||||||
|
t := underlyingType(rV2T(args[0]).t)
|
||||||
|
switch v := rV2V(args[0]).(type) {
|
||||||
|
case array:
|
||||||
|
return makeReflectValue(t.(*types.Array).Elt, v[i])
|
||||||
|
case []value:
|
||||||
|
return makeReflectValue(t.(*types.Slice).Elt, v[i])
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("reflect.(Value).Index(%T)", v))
|
||||||
|
}
|
||||||
|
return nil // unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰Bool(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (reflect.Value) bool
|
||||||
|
return rV2V(args[0]).(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰CanAddr(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (v reflect.Value) bool
|
||||||
|
// Always false for our representation.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰CanInterface(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (v reflect.Value) bool
|
||||||
|
// Always true for our representation.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰Elem(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (v reflect.Value) reflect.Value
|
||||||
|
switch x := rV2V(args[0]).(type) {
|
||||||
|
case iface:
|
||||||
|
return makeReflectValue(x.t, x.v)
|
||||||
|
case *value:
|
||||||
|
return makeReflectValue(underlyingType(rV2T(args[0]).t).(*types.Pointer).Base, *x)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("reflect.(Value).Elem(%T)", x))
|
||||||
|
}
|
||||||
|
return nil // unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰Field(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (v reflect.Value, i int) reflect.Value
|
||||||
|
v := args[0]
|
||||||
|
i := args[1].(int)
|
||||||
|
return makeReflectValue(underlyingType(rV2T(v).t).(*types.Struct).Fields[i].Type, rV2V(v).(structure)[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰Interface(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (v reflect.Value) interface{}
|
||||||
|
return ext۰reflect۰valueInterface(fn, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰Int(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (reflect.Value) int64
|
||||||
|
switch x := rV2V(args[0]).(type) {
|
||||||
|
case int:
|
||||||
|
return int64(x)
|
||||||
|
case int8:
|
||||||
|
return int64(x)
|
||||||
|
case int16:
|
||||||
|
return int64(x)
|
||||||
|
case int32:
|
||||||
|
return int64(x)
|
||||||
|
case int64:
|
||||||
|
return x
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("reflect.(Value).Int(%T)", x))
|
||||||
|
}
|
||||||
|
return nil // unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰IsNil(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (reflect.Value) bool
|
||||||
|
switch x := rV2V(args[0]).(type) {
|
||||||
|
case *value:
|
||||||
|
return x == nil
|
||||||
|
case chan value:
|
||||||
|
return x == nil
|
||||||
|
case map[value]value:
|
||||||
|
return x == nil
|
||||||
|
case *hashmap:
|
||||||
|
return x == nil
|
||||||
|
case iface:
|
||||||
|
return x.t == nil
|
||||||
|
case []value:
|
||||||
|
return x == nil
|
||||||
|
case *ssa.Function:
|
||||||
|
return x == nil
|
||||||
|
case *ssa.Builtin:
|
||||||
|
return x == nil
|
||||||
|
case *closure:
|
||||||
|
return x == nil
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("reflect.(Value).IsNil(%T)", x))
|
||||||
|
}
|
||||||
|
return nil // unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰Value۰IsValid(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (reflect.Value) bool
|
||||||
|
return rV2V(args[0]) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰valueInterface(fn *ssa.Function, args []value) value {
|
||||||
|
// Signature: func (v reflect.Value, safe bool) interface{}
|
||||||
|
v := args[0].(structure)
|
||||||
|
return iface{rV2T(v).t, rV2V(v)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ext۰reflect۰error۰Error(fn *ssa.Function, args []value) value {
|
||||||
|
return args[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// newMethod creates a new method of the specified name, package and receiver type.
|
||||||
|
func newMethod(pkg *ssa.Package, recvType types.Type, name string) *ssa.Function {
|
||||||
|
fn := &ssa.Function{
|
||||||
|
Name_: name,
|
||||||
|
Pkg: pkg,
|
||||||
|
Prog: pkg.Prog,
|
||||||
|
}
|
||||||
|
// TODO(adonovan): fix: hack: currently the only part of Signature
|
||||||
|
// that is needed is the "pointerness" of Recv.Type, and for
|
||||||
|
// now, we'll set it to always be false since we're only
|
||||||
|
// concerned with rtype. Encapsulate this better.
|
||||||
|
fn.Signature = &types.Signature{Recv: &types.Var{
|
||||||
|
Name: "recv",
|
||||||
|
Type: recvType,
|
||||||
|
}}
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func initReflect(i *interpreter) {
|
||||||
|
i.reflectPackage = &ssa.Package{
|
||||||
|
Prog: i.prog,
|
||||||
|
Types: reflectTypesPackage,
|
||||||
|
Members: make(map[string]ssa.Member),
|
||||||
|
}
|
||||||
|
|
||||||
|
i.rtypeMethods = ssa.MethodSet{
|
||||||
|
ssa.Id{nil, "Bits"}: newMethod(i.reflectPackage, rtypeType, "Bits"),
|
||||||
|
ssa.Id{nil, "Elem"}: newMethod(i.reflectPackage, rtypeType, "Elem"),
|
||||||
|
ssa.Id{nil, "Kind"}: newMethod(i.reflectPackage, rtypeType, "Kind"),
|
||||||
|
ssa.Id{nil, "NumOut"}: newMethod(i.reflectPackage, rtypeType, "NumOut"),
|
||||||
|
ssa.Id{nil, "Out"}: newMethod(i.reflectPackage, rtypeType, "Out"),
|
||||||
|
ssa.Id{nil, "String"}: newMethod(i.reflectPackage, rtypeType, "String"),
|
||||||
|
}
|
||||||
|
i.errorMethods = ssa.MethodSet{
|
||||||
|
ssa.Id{nil, "Error"}: newMethod(i.reflectPackage, errorType, "Error"),
|
||||||
|
}
|
||||||
|
}
|
447
ssa/interp/testdata/coverage.go
vendored
Normal file
447
ssa/interp/testdata/coverage.go
vendored
Normal file
@ -0,0 +1,447 @@
|
|||||||
|
// This interpreter test is designed to run very quickly yet provide
|
||||||
|
// some coverage of a broad selection of constructs.
|
||||||
|
// TODO(adonovan): more.
|
||||||
|
//
|
||||||
|
// Validate this file with 'go run' after editing.
|
||||||
|
// TODO(adonovan): break this into small files organized by theme.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
const zero int = 1
|
||||||
|
|
||||||
|
var v = []int{1 + zero: 42}
|
||||||
|
|
||||||
|
// Nonliteral keys in composite literal.
|
||||||
|
func init() {
|
||||||
|
if x := fmt.Sprint(v); x != "[0 0 42]" {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Call of variadic function with (implicit) empty slice.
|
||||||
|
if x := fmt.Sprint(); x != "" {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type empty interface{}
|
||||||
|
|
||||||
|
type I interface {
|
||||||
|
f() int
|
||||||
|
}
|
||||||
|
|
||||||
|
type T struct{ z int }
|
||||||
|
|
||||||
|
func (t T) f() int { return t.z }
|
||||||
|
|
||||||
|
func use(interface{}) {}
|
||||||
|
|
||||||
|
var counter = 2
|
||||||
|
|
||||||
|
// Test initialization, including init blocks containing 'return'.
|
||||||
|
// Assertion is in main.
|
||||||
|
func init() {
|
||||||
|
counter *= 3
|
||||||
|
return
|
||||||
|
counter *= 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
counter *= 5
|
||||||
|
return
|
||||||
|
counter *= 5
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursion.
|
||||||
|
func fib(x int) int {
|
||||||
|
if x < 2 {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return fib(x-1) + fib(x-2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fibgen(ch chan int) {
|
||||||
|
for x := 0; x < 10; x++ {
|
||||||
|
ch <- fib(x)
|
||||||
|
}
|
||||||
|
close(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Goroutines and channels.
|
||||||
|
func init() {
|
||||||
|
ch := make(chan int)
|
||||||
|
go fibgen(ch)
|
||||||
|
var fibs []int
|
||||||
|
for v := range ch {
|
||||||
|
fibs = append(fibs, v)
|
||||||
|
if len(fibs) == 10 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if x := fmt.Sprint(fibs); x != "[0 1 1 2 3 5 8 13 21 34]" {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test of aliasing.
|
||||||
|
func init() {
|
||||||
|
type S struct {
|
||||||
|
a, b string
|
||||||
|
}
|
||||||
|
|
||||||
|
s1 := []string{"foo", "bar"}
|
||||||
|
s2 := s1 // creates an alias
|
||||||
|
s2[0] = "wiz"
|
||||||
|
if x := fmt.Sprint(s1, s2); x != "[wiz bar] [wiz bar]" {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
pa1 := &[2]string{"foo", "bar"}
|
||||||
|
pa2 := pa1 // creates an alias
|
||||||
|
(*pa2)[0] = "wiz" // * required to workaround typechecker bug
|
||||||
|
if x := fmt.Sprint(*pa1, *pa2); x != "[wiz bar] [wiz bar]" {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
a1 := [2]string{"foo", "bar"}
|
||||||
|
a2 := a1 // creates a copy
|
||||||
|
a2[0] = "wiz"
|
||||||
|
if x := fmt.Sprint(a1, a2); x != "[foo bar] [wiz bar]" {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
t1 := S{"foo", "bar"}
|
||||||
|
t2 := t1 // copy
|
||||||
|
t2.a = "wiz"
|
||||||
|
if x := fmt.Sprint(t1, t2); x != "{foo bar} {wiz bar}" {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range over string.
|
||||||
|
func init() {
|
||||||
|
if x := len("Hello, 世界"); x != 13 { // bytes
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
var indices []int
|
||||||
|
var runes []rune
|
||||||
|
for i, r := range "Hello, 世界" {
|
||||||
|
runes = append(runes, r)
|
||||||
|
indices = append(indices, i)
|
||||||
|
}
|
||||||
|
if x := fmt.Sprint(runes); x != "[72 101 108 108 111 44 32 19990 30028]" {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
if x := fmt.Sprint(indices); x != "[0 1 2 3 4 5 6 7 10]" {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
s := ""
|
||||||
|
for _, r := range runes {
|
||||||
|
s = fmt.Sprintf("%s%c", s, r)
|
||||||
|
}
|
||||||
|
if s != "Hello, 世界" {
|
||||||
|
panic(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if counter != 2*3*5 {
|
||||||
|
panic(counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test builtins (e.g. complex) preserve named argument types.
|
||||||
|
type N complex128
|
||||||
|
var n N
|
||||||
|
n = complex(1.0, 2.0)
|
||||||
|
if n != complex(1.0, 2.0) {
|
||||||
|
panic(n)
|
||||||
|
}
|
||||||
|
if x := reflect.TypeOf(n).String(); x != "main.N" {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
if real(n) != 1.0 || imag(n) != 2.0 {
|
||||||
|
panic(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel + select.
|
||||||
|
ch := make(chan int, 1)
|
||||||
|
select {
|
||||||
|
case ch <- 1:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
panic("couldn't send")
|
||||||
|
}
|
||||||
|
if <-ch != 1 {
|
||||||
|
panic("couldn't receive")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anon structs with methods.
|
||||||
|
anon := struct{ T }{T: T{z: 1}}
|
||||||
|
if x := anon.f(); x != 1 {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
var i I = anon
|
||||||
|
if x := i.f(); x != 1 {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
// NB. precise output of reflect.Type.String is undefined.
|
||||||
|
if x := reflect.TypeOf(i).String(); x != "struct { main.T }" && x != "struct{main.T}" {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.
|
||||||
|
const message = "Hello, World!"
|
||||||
|
if fmt.Sprintf("%s, %s!", "Hello", "World") != message {
|
||||||
|
panic("oops")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type assertion.
|
||||||
|
type S struct {
|
||||||
|
f int
|
||||||
|
}
|
||||||
|
var e empty = S{f: 42}
|
||||||
|
switch v := e.(type) {
|
||||||
|
case S:
|
||||||
|
if v.f != 42 {
|
||||||
|
panic(v.f)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(reflect.TypeOf(v))
|
||||||
|
}
|
||||||
|
if i, ok := e.(I); ok {
|
||||||
|
panic(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch.
|
||||||
|
var x int
|
||||||
|
switch x {
|
||||||
|
case 1:
|
||||||
|
panic(x)
|
||||||
|
fallthrough
|
||||||
|
case 2, 3:
|
||||||
|
panic(x)
|
||||||
|
default:
|
||||||
|
// ok
|
||||||
|
}
|
||||||
|
// empty switch
|
||||||
|
switch {
|
||||||
|
}
|
||||||
|
// empty switch
|
||||||
|
switch {
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
// empty switch
|
||||||
|
switch {
|
||||||
|
default:
|
||||||
|
fallthrough
|
||||||
|
}
|
||||||
|
|
||||||
|
// string -> []rune conversion.
|
||||||
|
use([]rune("foo"))
|
||||||
|
|
||||||
|
// Calls of form x.f().
|
||||||
|
type S2 struct {
|
||||||
|
f func() int
|
||||||
|
}
|
||||||
|
S2{f: func() int { return 1 }}.f() // field is a func value
|
||||||
|
T{}.f() // method call
|
||||||
|
i.f() // interface method invocation
|
||||||
|
(interface {
|
||||||
|
f() int
|
||||||
|
}(T{})).f() // anon interface method invocation
|
||||||
|
|
||||||
|
// Map lookup.
|
||||||
|
if v, ok := map[string]string{}["foo5"]; v != "" || ok {
|
||||||
|
panic("oops")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple closures.
|
||||||
|
func init() {
|
||||||
|
b := 3
|
||||||
|
f := func(a int) int {
|
||||||
|
return a + b
|
||||||
|
}
|
||||||
|
b++
|
||||||
|
if x := f(1); x != 5 { // 1+4 == 5
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
b++
|
||||||
|
if x := f(2); x != 7 { // 2+5 == 7
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
if b := f(1) < 16 || f(2) < 17; !b {
|
||||||
|
panic("oops")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var order []int
|
||||||
|
|
||||||
|
func create(x int) int {
|
||||||
|
order = append(order, x)
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
var c = create(b + 1)
|
||||||
|
var a, b = create(1), create(2)
|
||||||
|
|
||||||
|
// Initialization order of package-level value specs.
|
||||||
|
func init() {
|
||||||
|
if x := fmt.Sprint(order); x != "[2 3 1]" {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
if c != 3 {
|
||||||
|
panic(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shifts.
|
||||||
|
func init() {
|
||||||
|
var i int64 = 1
|
||||||
|
var u uint64 = 1 << 32
|
||||||
|
if x := i << uint32(u); x != 1 {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
if x := i << uint64(u); x != 0 {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implicit conversion of delete() key operand.
|
||||||
|
func init() {
|
||||||
|
type I interface{}
|
||||||
|
m := make(map[I]bool)
|
||||||
|
m[1] = true
|
||||||
|
m[I(2)] = true
|
||||||
|
if len(m) != 2 {
|
||||||
|
panic(m)
|
||||||
|
}
|
||||||
|
delete(m, I(1))
|
||||||
|
delete(m, 2)
|
||||||
|
if len(m) != 0 {
|
||||||
|
panic(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Variadic bridge methods and interface thunks.
|
||||||
|
|
||||||
|
type VT int
|
||||||
|
|
||||||
|
var vcount = 0
|
||||||
|
|
||||||
|
func (VT) f(x int, y ...string) {
|
||||||
|
vcount++
|
||||||
|
if x != 1 {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
if len(y) != 2 || y[0] != "foo" || y[1] != "bar" {
|
||||||
|
panic(y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type VS struct {
|
||||||
|
VT
|
||||||
|
}
|
||||||
|
|
||||||
|
type VI interface {
|
||||||
|
f(x int, y ...string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
foobar := []string{"foo", "bar"}
|
||||||
|
var s VS
|
||||||
|
s.f(1, "foo", "bar")
|
||||||
|
s.f(1, foobar...)
|
||||||
|
if vcount != 2 {
|
||||||
|
panic("s.f not called twice")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn := VI.f
|
||||||
|
fn(s, 1, "foo", "bar")
|
||||||
|
fn(s, 1, foobar...)
|
||||||
|
if vcount != 4 {
|
||||||
|
panic("I.f not called twice")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple labels on same statement.
|
||||||
|
func multipleLabels() {
|
||||||
|
var trace []int
|
||||||
|
i := 0
|
||||||
|
one:
|
||||||
|
two:
|
||||||
|
for ; i < 3; i++ {
|
||||||
|
trace = append(trace, i)
|
||||||
|
switch i {
|
||||||
|
case 0:
|
||||||
|
continue two
|
||||||
|
case 1:
|
||||||
|
i++
|
||||||
|
goto one
|
||||||
|
case 2:
|
||||||
|
break two
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if x := fmt.Sprint(trace); x != "[0 1 2]" {
|
||||||
|
panic(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
multipleLabels()
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// Defer
|
||||||
|
|
||||||
|
func deferMutatesResults(noArgReturn bool) (a, b int) {
|
||||||
|
defer func() {
|
||||||
|
if a != 1 || b != 2 {
|
||||||
|
panic(fmt.Sprint(a, b))
|
||||||
|
}
|
||||||
|
a, b = 3, 4
|
||||||
|
}()
|
||||||
|
if noArgReturn {
|
||||||
|
a, b = 1, 2
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return 1, 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
a, b := deferMutatesResults(true)
|
||||||
|
if a != 3 || b != 4 {
|
||||||
|
panic(fmt.Sprint(a, b))
|
||||||
|
}
|
||||||
|
a, b = deferMutatesResults(false)
|
||||||
|
if a != 3 || b != 4 {
|
||||||
|
panic(fmt.Sprint(a, b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We concatenate init blocks to make a single function, but we must
|
||||||
|
// run defers at the end of each block, not the combined function.
|
||||||
|
var deferCount = 0
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
deferCount = 1
|
||||||
|
defer func() {
|
||||||
|
deferCount++
|
||||||
|
}()
|
||||||
|
// defer runs HERE
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Strictly speaking the spec says deferCount may be 0 or 2
|
||||||
|
// since the relative order of init blocks is unspecified.
|
||||||
|
if deferCount != 2 {
|
||||||
|
panic(deferCount) // defer call has not run!
|
||||||
|
}
|
||||||
|
}
|
65
ssa/interp/testdata/mrvchain.go
vendored
Normal file
65
ssa/interp/testdata/mrvchain.go
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Tests of call chaining f(g()) when g has multiple return values (MRVs).
|
||||||
|
// See https://code.google.com/p/go/issues/detail?id=4573.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func assert(actual, expected int) {
|
||||||
|
if actual != expected {
|
||||||
|
panic(actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func g() (int, int) {
|
||||||
|
return 5, 7
|
||||||
|
}
|
||||||
|
|
||||||
|
func g2() (float64, float64) {
|
||||||
|
return 5, 7
|
||||||
|
}
|
||||||
|
|
||||||
|
func f1v(x int, v ...int) {
|
||||||
|
assert(x, 5)
|
||||||
|
assert(v[0], 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
func f2(x, y int) {
|
||||||
|
assert(x, 5)
|
||||||
|
assert(y, 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
func f2v(x, y int, v ...int) {
|
||||||
|
assert(x, 5)
|
||||||
|
assert(y, 7)
|
||||||
|
assert(len(v), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func complexArgs() (float64, float64) {
|
||||||
|
return 5, 7
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendArgs() ([]string, string) {
|
||||||
|
return []string{"foo"}, "bar"
|
||||||
|
}
|
||||||
|
|
||||||
|
func h() (interface{}, bool) {
|
||||||
|
m := map[int]string{1: "hi"}
|
||||||
|
return m[1] // string->interface{} conversion within multi-valued expression
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f1v(g())
|
||||||
|
f2(g())
|
||||||
|
f2v(g())
|
||||||
|
// TODO(gri): the typechecker still doesn't support these cases correctly.
|
||||||
|
// if c := complex(complexArgs()); c != 5+7i {
|
||||||
|
// panic(c)
|
||||||
|
// }
|
||||||
|
// if s := append(appendArgs()); len(s) != 2 || s[0] != "foo" || s[1] != "bar" {
|
||||||
|
// panic(s)
|
||||||
|
// }
|
||||||
|
|
||||||
|
i, ok := h()
|
||||||
|
if !ok || i.(string) != "hi" {
|
||||||
|
panic(i)
|
||||||
|
}
|
||||||
|
}
|
466
ssa/interp/value.go
Normal file
466
ssa/interp/value.go
Normal file
@ -0,0 +1,466 @@
|
|||||||
|
package interp
|
||||||
|
|
||||||
|
// Values
|
||||||
|
//
|
||||||
|
// All interpreter values are "boxed" in the empty interface, value.
|
||||||
|
// The range of possible dynamic types within value are:
|
||||||
|
//
|
||||||
|
// - bool
|
||||||
|
// - numbers (all built-in int/float/complex types are distinguished)
|
||||||
|
// - string
|
||||||
|
// - map[value]value --- maps for which usesBuiltinMap(keyType)
|
||||||
|
// *hashmap --- maps for which !usesBuiltinMap(keyType)
|
||||||
|
// - chan value
|
||||||
|
// - []value --- slices
|
||||||
|
// - iface --- interfaces.
|
||||||
|
// - structure --- structs. Fields are ordered and accessed by numeric indices.
|
||||||
|
// - array --- arrays.
|
||||||
|
// - *value --- pointers. Careful: *value is a distinct type from *array etc.
|
||||||
|
// - *ssa.Function \
|
||||||
|
// *ssa.Builtin } --- functions.
|
||||||
|
// *closure /
|
||||||
|
// - tuple --- as returned by Ret, Next, "value,ok" modes, etc.
|
||||||
|
// - iter --- iterators from 'range' over map or string.
|
||||||
|
// - bad --- a poison pill for locals that have gone out of scope.
|
||||||
|
// - rtype -- the interpreter's concrete implementation of reflect.Type
|
||||||
|
//
|
||||||
|
// Note that nil is not on this list.
|
||||||
|
//
|
||||||
|
// Pay close attention to whether or not the dynamic type is a pointer.
|
||||||
|
// The compiler cannot help you since value is an empty interface.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/go/types"
|
||||||
|
"code.google.com/p/go.tools/ssa"
|
||||||
|
)
|
||||||
|
|
||||||
|
type value interface{}
|
||||||
|
|
||||||
|
type tuple []value
|
||||||
|
|
||||||
|
type array []value
|
||||||
|
|
||||||
|
type iface struct {
|
||||||
|
t types.Type // never an "untyped" type
|
||||||
|
v value
|
||||||
|
}
|
||||||
|
|
||||||
|
type structure []value
|
||||||
|
|
||||||
|
// For map, array, *array, slice, string or channel.
|
||||||
|
type iter interface {
|
||||||
|
// next returns a Tuple (key, value, ok).
|
||||||
|
// key and value are unaliased, e.g. copies of the sequence element.
|
||||||
|
next() tuple
|
||||||
|
}
|
||||||
|
|
||||||
|
type closure struct {
|
||||||
|
Fn *ssa.Function
|
||||||
|
Env []value
|
||||||
|
}
|
||||||
|
|
||||||
|
type bad struct{}
|
||||||
|
|
||||||
|
type rtype struct {
|
||||||
|
t types.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash functions and equivalence relation:
|
||||||
|
|
||||||
|
// hashString computes the FNV hash of s.
|
||||||
|
func hashString(s string) int {
|
||||||
|
var h uint32
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
h ^= uint32(s[i])
|
||||||
|
h *= 16777619
|
||||||
|
}
|
||||||
|
return int(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashType returns a hash for t such that
|
||||||
|
// types.IsIdentical(x, y) => hashType(x) == hashType(y).
|
||||||
|
func hashType(t types.Type) int {
|
||||||
|
return hashString(t.String()) // TODO(gri): provide a better hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// usesBuiltinMap returns true if the built-in hash function and
|
||||||
|
// equivalence relation for type t are consistent with those of the
|
||||||
|
// interpreter's representation of type t. Such types are: all basic
|
||||||
|
// types (bool, numbers, string), pointers and channels.
|
||||||
|
//
|
||||||
|
// usesBuiltinMap returns false for types that require a custom map
|
||||||
|
// implementation: interfaces, arrays and structs.
|
||||||
|
//
|
||||||
|
// Panic ensues if t is an invalid map key type: function, map or slice.
|
||||||
|
func usesBuiltinMap(t types.Type) bool {
|
||||||
|
switch t := t.(type) {
|
||||||
|
case *types.Basic, *types.Chan, *types.Pointer:
|
||||||
|
return true
|
||||||
|
case *types.NamedType:
|
||||||
|
return usesBuiltinMap(t.Underlying)
|
||||||
|
case *types.Interface, *types.Array, *types.Struct:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("invalid map key type: %T", t))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x array) eq(_y interface{}) bool {
|
||||||
|
y := _y.(array)
|
||||||
|
for i, xi := range x {
|
||||||
|
if !equals(xi, y[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x array) hash() int {
|
||||||
|
h := 0
|
||||||
|
for _, xi := range x {
|
||||||
|
h += hash(xi)
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x structure) eq(_y interface{}) bool {
|
||||||
|
y := _y.(structure)
|
||||||
|
// TODO(adonovan): fix: only non-blank fields should be
|
||||||
|
// compared. This requires that we have type information
|
||||||
|
// available from the enclosing == operation or map access;
|
||||||
|
// the value is not sufficient.
|
||||||
|
for i, xi := range x {
|
||||||
|
if !equals(xi, y[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x structure) hash() int {
|
||||||
|
h := 0
|
||||||
|
for _, xi := range x {
|
||||||
|
h += hash(xi)
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x iface) eq(_y interface{}) bool {
|
||||||
|
y := _y.(iface)
|
||||||
|
return types.IsIdentical(x.t, y.t) && (x.t == nil || equals(x.v, y.v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x iface) hash() int {
|
||||||
|
return hashType(x.t)*8581 + hash(x.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x rtype) hash() int {
|
||||||
|
return hashType(x.t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x rtype) eq(y interface{}) bool {
|
||||||
|
return types.IsIdentical(x.t, y.(rtype).t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// equals returns true iff x and y are equal according to Go's
|
||||||
|
// linguistic equivalence relation. In a well-typed program, the
|
||||||
|
// types of x and y are guaranteed equal.
|
||||||
|
func equals(x, y value) bool {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case bool:
|
||||||
|
return x == y.(bool)
|
||||||
|
case int:
|
||||||
|
return x == y.(int)
|
||||||
|
case int8:
|
||||||
|
return x == y.(int8)
|
||||||
|
case int16:
|
||||||
|
return x == y.(int16)
|
||||||
|
case int32:
|
||||||
|
return x == y.(int32)
|
||||||
|
case int64:
|
||||||
|
return x == y.(int64)
|
||||||
|
case uint:
|
||||||
|
return x == y.(uint)
|
||||||
|
case uint8:
|
||||||
|
return x == y.(uint8)
|
||||||
|
case uint16:
|
||||||
|
return x == y.(uint16)
|
||||||
|
case uint32:
|
||||||
|
return x == y.(uint32)
|
||||||
|
case uint64:
|
||||||
|
return x == y.(uint64)
|
||||||
|
case uintptr:
|
||||||
|
return x == y.(uintptr)
|
||||||
|
case float32:
|
||||||
|
return x == y.(float32)
|
||||||
|
case float64:
|
||||||
|
return x == y.(float64)
|
||||||
|
case complex64:
|
||||||
|
return x == y.(complex64)
|
||||||
|
case complex128:
|
||||||
|
return x == y.(complex128)
|
||||||
|
case string:
|
||||||
|
return x == y.(string)
|
||||||
|
case *value:
|
||||||
|
return x == y.(*value)
|
||||||
|
case chan value:
|
||||||
|
return x == y.(chan value)
|
||||||
|
case structure:
|
||||||
|
return x.eq(y)
|
||||||
|
case array:
|
||||||
|
return x.eq(y)
|
||||||
|
case iface:
|
||||||
|
return x.eq(y)
|
||||||
|
case rtype:
|
||||||
|
return x.eq(y)
|
||||||
|
|
||||||
|
// Since the following types don't support comparison,
|
||||||
|
// these cases are only reachable if one of x or y is
|
||||||
|
// (literally) nil.
|
||||||
|
case *hashmap:
|
||||||
|
return x == y.(*hashmap)
|
||||||
|
case map[value]value:
|
||||||
|
return (x != nil) == (y.(map[value]value) != nil)
|
||||||
|
case *ssa.Function, *closure:
|
||||||
|
return x == y
|
||||||
|
case []value:
|
||||||
|
return (x != nil) == (y.([]value) != nil)
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("comparing incomparable type %T", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns an integer hash of x such that equals(x, y) => hash(x) == hash(y).
|
||||||
|
func hash(x value) int {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case bool:
|
||||||
|
if x {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
case int:
|
||||||
|
return x
|
||||||
|
case int8:
|
||||||
|
return int(x)
|
||||||
|
case int16:
|
||||||
|
return int(x)
|
||||||
|
case int32:
|
||||||
|
return int(x)
|
||||||
|
case int64:
|
||||||
|
return int(x)
|
||||||
|
case uint:
|
||||||
|
return int(x)
|
||||||
|
case uint8:
|
||||||
|
return int(x)
|
||||||
|
case uint16:
|
||||||
|
return int(x)
|
||||||
|
case uint32:
|
||||||
|
return int(x)
|
||||||
|
case uint64:
|
||||||
|
return int(x)
|
||||||
|
case uintptr:
|
||||||
|
return int(x)
|
||||||
|
case float32:
|
||||||
|
return int(x)
|
||||||
|
case float64:
|
||||||
|
return int(x)
|
||||||
|
case complex64:
|
||||||
|
return int(real(x))
|
||||||
|
case complex128:
|
||||||
|
return int(real(x))
|
||||||
|
case string:
|
||||||
|
return hashString(x)
|
||||||
|
case *value:
|
||||||
|
return int(uintptr(unsafe.Pointer(x)))
|
||||||
|
case chan value:
|
||||||
|
return int(uintptr(reflect.ValueOf(x).Pointer()))
|
||||||
|
case structure:
|
||||||
|
return x.hash()
|
||||||
|
case array:
|
||||||
|
return x.hash()
|
||||||
|
case iface:
|
||||||
|
return x.hash()
|
||||||
|
case rtype:
|
||||||
|
return x.hash()
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("%T is unhashable", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyVal returns a copy of value v.
|
||||||
|
// TODO(adonovan): add tests of aliasing and mutation.
|
||||||
|
func copyVal(v value) value {
|
||||||
|
if v == nil {
|
||||||
|
panic("copyVal(nil)")
|
||||||
|
}
|
||||||
|
switch v := v.(type) {
|
||||||
|
case bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, complex64, complex128, string, unsafe.Pointer:
|
||||||
|
return v
|
||||||
|
case map[value]value:
|
||||||
|
return v
|
||||||
|
case *hashmap:
|
||||||
|
return v
|
||||||
|
case chan value:
|
||||||
|
return v
|
||||||
|
case *value:
|
||||||
|
return v
|
||||||
|
case *ssa.Function, *ssa.Builtin, *closure:
|
||||||
|
return v
|
||||||
|
case iface:
|
||||||
|
return v
|
||||||
|
case []value:
|
||||||
|
return v
|
||||||
|
case structure:
|
||||||
|
a := make(structure, len(v))
|
||||||
|
copy(a, v)
|
||||||
|
return a
|
||||||
|
case array:
|
||||||
|
a := make(array, len(v))
|
||||||
|
copy(a, v)
|
||||||
|
return a
|
||||||
|
case tuple:
|
||||||
|
break
|
||||||
|
case rtype:
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("cannot copy %T", v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints in the style of built-in println.
|
||||||
|
// (More or less; in gc println is actually a compiler intrinsic and
|
||||||
|
// can distinguish println(1) from println(interface{}(1)).)
|
||||||
|
func toWriter(w io.Writer, v value) {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case nil, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, complex64, complex128, string:
|
||||||
|
fmt.Fprintf(w, "%v", v)
|
||||||
|
|
||||||
|
case map[value]value:
|
||||||
|
io.WriteString(w, "map[")
|
||||||
|
sep := " "
|
||||||
|
for k, e := range v {
|
||||||
|
io.WriteString(w, sep)
|
||||||
|
sep = " "
|
||||||
|
toWriter(w, k)
|
||||||
|
io.WriteString(w, ":")
|
||||||
|
toWriter(w, e)
|
||||||
|
}
|
||||||
|
io.WriteString(w, "]")
|
||||||
|
|
||||||
|
case *hashmap:
|
||||||
|
io.WriteString(w, "map[")
|
||||||
|
sep := " "
|
||||||
|
for _, e := range v.table {
|
||||||
|
for e != nil {
|
||||||
|
io.WriteString(w, sep)
|
||||||
|
sep = " "
|
||||||
|
toWriter(w, e.key)
|
||||||
|
io.WriteString(w, ":")
|
||||||
|
toWriter(w, e.value)
|
||||||
|
e = e.next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
io.WriteString(w, "]")
|
||||||
|
|
||||||
|
case chan value:
|
||||||
|
fmt.Fprintf(w, "%v", v) // (an address)
|
||||||
|
|
||||||
|
case *value:
|
||||||
|
if v == nil {
|
||||||
|
io.WriteString(w, "<nil>")
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, "%p", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
case iface:
|
||||||
|
toWriter(w, v.v)
|
||||||
|
|
||||||
|
case structure:
|
||||||
|
io.WriteString(w, "{")
|
||||||
|
for i, e := range v {
|
||||||
|
if i > 0 {
|
||||||
|
io.WriteString(w, " ")
|
||||||
|
}
|
||||||
|
toWriter(w, e)
|
||||||
|
}
|
||||||
|
io.WriteString(w, "}")
|
||||||
|
|
||||||
|
case array:
|
||||||
|
io.WriteString(w, "[")
|
||||||
|
for i, e := range v {
|
||||||
|
if i > 0 {
|
||||||
|
io.WriteString(w, " ")
|
||||||
|
}
|
||||||
|
toWriter(w, e)
|
||||||
|
}
|
||||||
|
io.WriteString(w, "]")
|
||||||
|
|
||||||
|
case []value:
|
||||||
|
io.WriteString(w, "[")
|
||||||
|
for i, e := range v {
|
||||||
|
if i > 0 {
|
||||||
|
io.WriteString(w, " ")
|
||||||
|
}
|
||||||
|
toWriter(w, e)
|
||||||
|
}
|
||||||
|
io.WriteString(w, "]")
|
||||||
|
|
||||||
|
case *ssa.Function, *ssa.Builtin, *closure:
|
||||||
|
fmt.Fprintf(w, "%p", v) // (an address)
|
||||||
|
|
||||||
|
case rtype:
|
||||||
|
io.WriteString(w, v.t.String())
|
||||||
|
|
||||||
|
case tuple:
|
||||||
|
// Unreachable in well-formed Go programs
|
||||||
|
io.WriteString(w, "(")
|
||||||
|
for i, e := range v {
|
||||||
|
if i > 0 {
|
||||||
|
io.WriteString(w, ", ")
|
||||||
|
}
|
||||||
|
toWriter(w, e)
|
||||||
|
}
|
||||||
|
io.WriteString(w, ")")
|
||||||
|
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(w, "<%T>", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements printing of Go values in the style of built-in println.
|
||||||
|
func toString(v value) string {
|
||||||
|
var b bytes.Buffer
|
||||||
|
toWriter(&b, v)
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// Iterators
|
||||||
|
|
||||||
|
type stringIter struct {
|
||||||
|
*strings.Reader
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *stringIter) next() tuple {
|
||||||
|
okv := make(tuple, 3)
|
||||||
|
ch, n, err := it.ReadRune()
|
||||||
|
ok := err != io.EOF
|
||||||
|
okv[0] = ok
|
||||||
|
if ok {
|
||||||
|
okv[1] = it.i
|
||||||
|
okv[2] = ch
|
||||||
|
}
|
||||||
|
it.i += n
|
||||||
|
return okv
|
||||||
|
}
|
||||||
|
|
||||||
|
type mapIter chan [2]value
|
||||||
|
|
||||||
|
func (it mapIter) next() tuple {
|
||||||
|
kv, ok := <-it
|
||||||
|
return tuple{ok, kv[0], kv[1]}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user