Commit 5d15963a authored by Robert Griesemer's avatar Robert Griesemer

exp/types/staging: filling in more blanks

- simplified assignment checking by removing duplicate code
- implemented field lookup (methods, structs, embedded fields)
- importing methods (not just parsing them)
- type-checking functions and methods
- typechecking more statements (inc/dec, select, return)
- tracing support for easier debugging
- handling nil more correctly (comparisons)
- initial support for [...]T{} arrays
- initial support for method expressions
- lots of bug fixes

All packages under pkg/go as well as pkg/exp/types typecheck
now with pkg/exp/gotype applied to them; i.e., a significant
amount of typechecking works now (several statements are not
implemented yet, but handling statements is almost trivial in
comparison with typechecking expressions).

R=rsc
CC=golang-dev
https://golang.org/cl/6768063
parent 3b04d23c
...@@ -22,7 +22,8 @@ func readGopackHeader(r *bufio.Reader) (name string, size int, err error) { ...@@ -22,7 +22,8 @@ func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
if err != nil { if err != nil {
return return
} }
if trace { // leave for debugging
if false {
fmt.Printf("header: %s", hdr) fmt.Printf("header: %s", hdr)
} }
s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10])) s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
......
...@@ -44,7 +44,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota ...@@ -44,7 +44,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
switch id { switch id {
case _Make, _New: case _Make, _New:
// argument must be a type // argument must be a type
typ0 = underlying(check.typ(arg0, false)) typ0 = check.typ(arg0, false)
if typ0 == Typ[Invalid] { if typ0 == Typ[Invalid] {
goto Error goto Error
} }
...@@ -191,7 +191,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota ...@@ -191,7 +191,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
case _Make: case _Make:
var min int // minimum number of arguments var min int // minimum number of arguments
switch typ0.(type) { switch underlying(typ0).(type) {
case *Slice: case *Slice:
min = 2 min = 2
case *Map, *Chan: case *Map, *Chan:
...@@ -301,7 +301,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota ...@@ -301,7 +301,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
var t operand var t operand
x1 := x x1 := x
for _, arg := range args { for _, arg := range args {
check.exprOrType(x1, arg, nil, iota, true) // permit trace for types, e.g.: new(trace(T)) check.rawExpr(x1, arg, nil, iota, true) // permit trace for types, e.g.: new(trace(T))
check.dump("%s: %s", x1.pos(), x1) check.dump("%s: %s", x1.pos(), x1)
x1 = &t // use incoming x only for first argument x1 = &t // use incoming x only for first argument
} }
......
...@@ -13,6 +13,9 @@ import ( ...@@ -13,6 +13,9 @@ import (
"sort" "sort"
) )
// enable for debugging
const trace = false
type checker struct { type checker struct {
fset *token.FileSet fset *token.FileSet
pkg *ast.Package pkg *ast.Package
...@@ -23,6 +26,8 @@ type checker struct { ...@@ -23,6 +26,8 @@ type checker struct {
firsterr error firsterr error
filenames []string // sorted list of package file names for reproducible iteration order filenames []string // sorted list of package file names for reproducible iteration order
initexprs map[*ast.ValueSpec][]ast.Expr // "inherited" initialization expressions for constant declarations initexprs map[*ast.ValueSpec][]ast.Expr // "inherited" initialization expressions for constant declarations
functypes []*Signature // stack of function signatures; actively typechecked function on top
pos []token.Pos // stack of expr positions; debugging support, used if trace is set
} }
// declare declares an object of the given kind and name (ident) in scope; // declare declares an object of the given kind and name (ident) in scope;
...@@ -57,17 +62,19 @@ func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident ...@@ -57,17 +62,19 @@ func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident
return return
} }
// determine type for all of lhs, if any
// (but only set it for the object we typecheck!)
var t Type var t Type
if typ != nil { if typ != nil {
t = check.typ(typ, false) t = check.typ(typ, false)
} }
// len(lhs) >= 1 // len(lhs) > 0
if len(lhs) == len(rhs) { if len(lhs) == len(rhs) {
// check only corresponding lhs and rhs // check only lhs and rhs corresponding to obj
var l, r ast.Expr var l, r ast.Expr
for i, ident := range lhs { for i, name := range lhs {
if ident.Obj == obj { if name.Obj == obj {
l = lhs[i] l = lhs[i]
r = rhs[i] r = rhs[i]
break break
...@@ -75,14 +82,17 @@ func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident ...@@ -75,14 +82,17 @@ func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident
} }
assert(l != nil) assert(l != nil)
obj.Type = t obj.Type = t
// check rhs check.assign1to1(l, r, nil, true, iota)
var x operand
check.expr(&x, r, t, iota)
// assign to lhs
check.assignment(l, &x, true)
return return
} }
// there must be a type or initialization expressions
if t == nil && len(rhs) == 0 {
check.invalidAST(pos, "missing type or initialization expression")
t = Typ[Invalid]
}
// if we have a type, mark all of lhs
if t != nil { if t != nil {
for _, name := range lhs { for _, name := range lhs {
name.Obj.Type = t name.Obj.Type = t
...@@ -100,18 +110,18 @@ func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident ...@@ -100,18 +110,18 @@ func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident
} }
} }
// ident type checks an identifier. func (check *checker) function(typ *Signature, body *ast.BlockStmt) {
func (check *checker) ident(name *ast.Ident, cycleOk bool) { check.functypes = append(check.functypes, typ)
obj := name.Obj check.stmt(body)
if obj == nil { check.functypes = check.functypes[0 : len(check.functypes)-1]
check.invalidAST(name.Pos(), "missing object for %s", name.Name) }
return
}
if obj.Type != nil { // object typechecks an object by assigning it a type; obj.Type must be nil.
// object has already been type checked // Callers must check obj.Type before calling object; this eliminates a call
return // for each identifier that has been typechecked already, a common scenario.
} //
func (check *checker) object(obj *ast.Object, cycleOk bool) {
assert(obj.Type == nil)
switch obj.Kind { switch obj.Kind {
case ast.Bad, ast.Pkg: case ast.Bad, ast.Pkg:
...@@ -128,6 +138,7 @@ func (check *checker) ident(name *ast.Ident, cycleOk bool) { ...@@ -128,6 +138,7 @@ func (check *checker) ident(name *ast.Ident, cycleOk bool) {
// Data == nil => the object's expression is being evaluated // Data == nil => the object's expression is being evaluated
if obj.Data == nil { if obj.Data == nil {
check.errorf(obj.Pos(), "illegal cycle in initialization of %s", obj.Name) check.errorf(obj.Pos(), "illegal cycle in initialization of %s", obj.Name)
obj.Type = Typ[Invalid]
return return
} }
spec := obj.Decl.(*ast.ValueSpec) spec := obj.Decl.(*ast.ValueSpec)
...@@ -144,45 +155,47 @@ func (check *checker) ident(name *ast.Ident, cycleOk bool) { ...@@ -144,45 +155,47 @@ func (check *checker) ident(name *ast.Ident, cycleOk bool) {
typ := &NamedType{Obj: obj} typ := &NamedType{Obj: obj}
obj.Type = typ // "mark" object so recursion terminates obj.Type = typ // "mark" object so recursion terminates
typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk)) typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk))
// collect associated methods, if any // typecheck associated method signatures
if obj.Data != nil { if obj.Data != nil {
scope := obj.Data.(*ast.Scope) scope := obj.Data.(*ast.Scope)
// struct fields must not conflict with methods switch t := typ.Underlying.(type) {
if t, ok := typ.Underlying.(*Struct); ok { case *Struct:
// struct fields must not conflict with methods
for _, f := range t.Fields { for _, f := range t.Fields {
if m := scope.Lookup(f.Name); m != nil { if m := scope.Lookup(f.Name); m != nil {
check.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, f.Name) check.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, f.Name)
} }
} }
} // ok to continue
// collect methods case *Interface:
methods := make(ObjList, len(scope.Objects)) // methods cannot be associated with an interface type
i := 0 for _, m := range scope.Objects {
for _, m := range scope.Objects {
methods[i] = m
i++
}
methods.Sort()
typ.Methods = methods
// methods cannot be associated with an interface type
// (do this check after sorting for reproducible error positions - needed for testing)
if _, ok := typ.Underlying.(*Interface); ok {
for _, m := range methods {
recv := m.Decl.(*ast.FuncDecl).Recv.List[0].Type recv := m.Decl.(*ast.FuncDecl).Recv.List[0].Type
check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name) check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name)
} }
// ok to continue
}
// typecheck method signatures
for _, m := range scope.Objects {
mdecl := m.Decl.(*ast.FuncDecl)
// TODO(gri) At the moment, the receiver is type-checked when checking
// the method body. Also, we don't properly track if the receiver is
// a pointer (i.e., currently, method sets are too large). FIX THIS.
mtyp := check.typ(mdecl.Type, cycleOk).(*Signature)
m.Type = mtyp
} }
} }
case ast.Fun: case ast.Fun:
fdecl := obj.Decl.(*ast.FuncDecl) fdecl := obj.Decl.(*ast.FuncDecl)
ftyp := check.typ(fdecl.Type, cycleOk).(*Signature)
obj.Type = ftyp
if fdecl.Recv != nil { if fdecl.Recv != nil {
// TODO(gri) is this good enough for the receiver? // This will ensure that the method base type is
// type-checked
check.collectFields(token.FUNC, fdecl.Recv, true) check.collectFields(token.FUNC, fdecl.Recv, true)
} }
check.stmt(fdecl.Body) ftyp := check.typ(fdecl.Type, cycleOk).(*Signature)
obj.Type = ftyp
check.function(ftyp, fdecl.Body)
default: default:
panic("unreachable") panic("unreachable")
...@@ -283,20 +296,28 @@ func (check *checker) decl(decl ast.Decl) { ...@@ -283,20 +296,28 @@ func (check *checker) decl(decl ast.Decl) {
// nothing to do (handled by ast.NewPackage) // nothing to do (handled by ast.NewPackage)
case *ast.ValueSpec: case *ast.ValueSpec:
for _, name := range s.Names { for _, name := range s.Names {
if name.Name == "_" { if obj := name.Obj; obj.Type == nil {
// TODO(gri) why is _ special here? check.object(obj, false)
} else {
check.ident(name, false)
} }
} }
case *ast.TypeSpec: case *ast.TypeSpec:
check.ident(s.Name, false) if obj := s.Name.Obj; obj.Type == nil {
check.object(obj, false)
}
default: default:
check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
} }
} }
case *ast.FuncDecl: case *ast.FuncDecl:
check.ident(d.Name, false) if d.Name.Name == "init" {
// initialization function
// TODO(gri) ignore for now (has no object associated with it)
// (should probably collect in a first phase and properly initialize)
return
}
if obj := d.Name.Obj; obj.Type == nil {
check.object(obj, false)
}
default: default:
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
} }
......
...@@ -54,7 +54,7 @@ var ( ...@@ -54,7 +54,7 @@ var (
zeroConst = int64(0) zeroConst = int64(0)
oneConst = int64(1) oneConst = int64(1)
minusOneConst = int64(-1) minusOneConst = int64(-1)
nilConst = new(nilType) nilConst = nilType{}
) )
// int64 bounds // int64 bounds
......
...@@ -29,7 +29,9 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota ...@@ -29,7 +29,9 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota
} }
// TODO(gri) fix this - implement all checks and constant evaluation // TODO(gri) fix this - implement all checks and constant evaluation
x.mode = value if x.mode != constant {
x.mode = value
}
x.expr = conv x.expr = conv
x.typ = typ x.typ = typ
return return
......
...@@ -13,10 +13,6 @@ import ( ...@@ -13,10 +13,6 @@ import (
"go/token" "go/token"
) )
// debugging flags
const debug = false
const trace = false
// TODO(gri) eventually assert and unimplemented should disappear. // TODO(gri) eventually assert and unimplemented should disappear.
func assert(p bool) { func assert(p bool) {
if !p { if !p {
...@@ -25,15 +21,40 @@ func assert(p bool) { ...@@ -25,15 +21,40 @@ func assert(p bool) {
} }
func unimplemented() { func unimplemented() {
if debug { // enable for debugging
panic("unimplemented") // panic("unimplemented")
}
} }
func unreachable() { func unreachable() {
panic("unreachable") panic("unreachable")
} }
func (check *checker) printTrace(format string, args []interface{}) {
const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "
n := len(check.pos) - 1
i := 2 * n
for i > len(dots) {
fmt.Print(dots)
i -= len(dots)
}
// i <= len(dots)
fmt.Printf("%s: ", 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 { func (check *checker) formatMsg(format string, args []interface{}) string {
for i, arg := range args { for i, arg := range args {
switch a := arg.(type) { switch a := arg.(type) {
......
...@@ -22,7 +22,8 @@ func readGopackHeader(r *bufio.Reader) (name string, size int, err error) { ...@@ -22,7 +22,8 @@ func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
if err != nil { if err != nil {
return return
} }
if trace { // leave for debugging
if false {
fmt.Printf("header: %s", hdr) fmt.Printf("header: %s", hdr)
} }
s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10])) s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
......
This diff is collapsed.
...@@ -84,10 +84,6 @@ func FindPkg(path, srcDir string) (filename, id string) { ...@@ -84,10 +84,6 @@ func FindPkg(path, srcDir string) (filename, id string) {
// in error messages. // in error messages.
// //
func GcImportData(imports map[string]*ast.Object, filename, id string, data *bufio.Reader) (pkg *ast.Object, err error) { func GcImportData(imports map[string]*ast.Object, filename, id string, data *bufio.Reader) (pkg *ast.Object, err error) {
if trace {
fmt.Printf("importing %s (%s)\n", id, filename)
}
// support for gcParser error handling // support for gcParser error handling
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
...@@ -185,7 +181,8 @@ func (p *gcParser) next() { ...@@ -185,7 +181,8 @@ func (p *gcParser) next() {
default: default:
p.lit = "" p.lit = ""
} }
if trace { // leave for debugging
if false {
fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit) fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
} }
} }
...@@ -202,7 +199,7 @@ func (p *gcParser) declare(scope *ast.Scope, kind ast.ObjKind, name string) *ast ...@@ -202,7 +199,7 @@ func (p *gcParser) declare(scope *ast.Scope, kind ast.ObjKind, name string) *ast
// otherwise create a new object and insert it into the package scope // otherwise create a new object and insert it into the package scope
obj := ast.NewObj(kind, name) obj := ast.NewObj(kind, name)
if scope.Insert(obj) != nil { if scope.Insert(obj) != nil {
p.errorf("already declared: %v %s", kind, obj.Name) unreachable() // Lookup should have found it
} }
// if the new type object is a named type it may be referred // if the new type object is a named type it may be referred
...@@ -397,6 +394,7 @@ func (p *gcParser) parseField() *StructField { ...@@ -397,6 +394,7 @@ func (p *gcParser) parseField() *StructField {
// anonymous field - typ must be T or *T and T must be a type 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 { if typ, ok := deref(f.Type).(*NamedType); ok && typ.Obj != nil {
f.Name = typ.Obj.Name f.Name = typ.Obj.Name
f.IsAnonymous = true
} else { } else {
p.errorf("anonymous field expected") p.errorf("anonymous field expected")
} }
...@@ -442,7 +440,7 @@ func (p *gcParser) parseParameter() (par *ast.Object, isVariadic bool) { ...@@ -442,7 +440,7 @@ func (p *gcParser) parseParameter() (par *ast.Object, isVariadic bool) {
ptyp := p.parseType() ptyp := p.parseType()
// ignore argument tag (e.g. "noescape") // ignore argument tag (e.g. "noescape")
if p.tok == scanner.String { if p.tok == scanner.String {
p.expect(scanner.String) p.next()
} }
par = ast.NewObj(ast.Var, name) par = ast.NewObj(ast.Var, name)
par.Type = ptyp par.Type = ptyp
...@@ -504,12 +502,12 @@ func (p *gcParser) parseSignature() *Signature { ...@@ -504,12 +502,12 @@ func (p *gcParser) parseSignature() *Signature {
} }
// InterfaceType = "interface" "{" [ MethodList ] "}" . // InterfaceType = "interface" "{" [ MethodList ] "}" .
// MethodList = Method { ";" Method } . // MethodList = Method { ";" Method } .
// Method = Name Signature . // Method = Name Signature .
// //
// (The methods of embedded interfaces are always "inlined" // The methods of embedded interfaces are always "inlined"
// by the compiler and thus embedded interfaces are never // by the compiler and thus embedded interfaces are never
// visible in the export data.) // visible in the export data.
// //
func (p *gcParser) parseInterfaceType() Type { func (p *gcParser) parseInterfaceType() Type {
var methods ObjList var methods ObjList
...@@ -558,11 +556,12 @@ func (p *gcParser) parseChanType() Type { ...@@ -558,11 +556,12 @@ func (p *gcParser) parseChanType() Type {
// BasicType | TypeName | ArrayType | SliceType | StructType | // BasicType | TypeName | ArrayType | SliceType | StructType |
// PointerType | FuncType | InterfaceType | MapType | ChanType | // PointerType | FuncType | InterfaceType | MapType | ChanType |
// "(" Type ")" . // "(" Type ")" .
// BasicType = ident . //
// TypeName = ExportedName . // BasicType = ident .
// SliceType = "[" "]" Type . // TypeName = ExportedName .
// SliceType = "[" "]" Type .
// PointerType = "*" Type . // PointerType = "*" Type .
// FuncType = "func" Signature . // FuncType = "func" Signature .
// //
func (p *gcParser) parseType() Type { func (p *gcParser) parseType() Type {
switch p.tok { switch p.tok {
...@@ -688,7 +687,7 @@ func (p *gcParser) parseNumber() (x operand) { ...@@ -688,7 +687,7 @@ func (p *gcParser) parseNumber() (x operand) {
// Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit . // Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit .
// bool_lit = "true" | "false" . // bool_lit = "true" | "false" .
// complex_lit = "(" float_lit "+" float_lit "i" ")" . // complex_lit = "(" float_lit "+" float_lit "i" ")" .
// rune_lit = "(" int_lit "+" int_lit ")" . // rune_lit = "(" int_lit "+" int_lit ")" .
// string_lit = `"` { unicode_char } `"` . // string_lit = `"` { unicode_char } `"` .
// //
func (p *gcParser) parseConstDecl() { func (p *gcParser) parseConstDecl() {
...@@ -783,45 +782,61 @@ func (p *gcParser) parseVarDecl() { ...@@ -783,45 +782,61 @@ func (p *gcParser) parseVarDecl() {
obj.Type = p.parseType() obj.Type = p.parseType()
} }
// FuncBody = "{" ... "}" . // Func = Signature [ Body ] .
// // Body = "{" ... "}" .
func (p *gcParser) parseFuncBody() {
p.expect('{')
for i := 1; i > 0; p.next() {
switch p.tok {
case '{':
i++
case '}':
i--
}
}
}
// FuncDecl = "func" ExportedName Signature [ FuncBody ] .
// //
func (p *gcParser) parseFuncDecl() { func (p *gcParser) parseFunc(scope *ast.Scope, name string) {
// "func" already consumed obj := p.declare(scope, ast.Fun, name)
pkg, name := p.parseExportedName()
obj := p.declare(pkg.Data.(*ast.Scope), ast.Fun, name)
obj.Type = p.parseSignature() obj.Type = p.parseSignature()
if p.tok == '{' { if p.tok == '{' {
p.parseFuncBody() p.next()
for i := 1; i > 0; p.next() {
switch p.tok {
case '{':
i++
case '}':
i--
}
}
} }
} }
// MethodDecl = "func" Receiver Name Signature . // MethodDecl = "func" Receiver Name Func .
// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" [ FuncBody ]. // Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" .
// //
func (p *gcParser) parseMethodDecl() { func (p *gcParser) parseMethodDecl() {
// "func" already consumed // "func" already consumed
p.expect('(') p.expect('(')
p.parseParameter() // receiver recv, _ := p.parseParameter() // receiver
p.expect(')') p.expect(')')
p.parseName() // unexported method names in imports are qualified with their package.
p.parseSignature() // determine receiver base type object
if p.tok == '{' { typ := recv.Type.(Type)
p.parseFuncBody() if ptr, ok := typ.(*Pointer); ok {
typ = ptr.Base
}
obj := typ.(*NamedType).Obj
// determine base type scope
var scope *ast.Scope
if obj.Data != nil {
scope = obj.Data.(*ast.Scope)
} else {
scope = ast.NewScope(nil)
obj.Data = scope
} }
// declare method in base type scope
name := p.parseName() // unexported method names in imports are qualified with their package.
p.parseFunc(scope, name)
}
// FuncDecl = "func" ExportedName Func .
//
func (p *gcParser) parseFuncDecl() {
// "func" already consumed
pkg, name := p.parseExportedName()
p.parseFunc(pkg.Data.(*ast.Scope), name)
} }
// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" . // Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
......
...@@ -69,7 +69,11 @@ func (x *operand) String() string { ...@@ -69,7 +69,11 @@ func (x *operand) String() string {
} }
buf.WriteString(operandModeString[x.mode]) buf.WriteString(operandModeString[x.mode])
if x.mode == constant { if x.mode == constant {
fmt.Fprintf(&buf, " %v", x.val) format := " %v"
if isString(x.typ) {
format = " %q"
}
fmt.Fprintf(&buf, format, x.val)
} }
if x.mode != novalue && (x.mode != constant || !isUntyped(x.typ)) { if x.mode != novalue && (x.mode != constant || !isUntyped(x.typ)) {
fmt.Fprintf(&buf, " of type %s", typeString(x.typ)) fmt.Fprintf(&buf, " of type %s", typeString(x.typ))
...@@ -125,6 +129,11 @@ func (x *operand) implements(T *Interface) bool { ...@@ -125,6 +129,11 @@ func (x *operand) implements(T *Interface) bool {
return true return true
} }
// isNil reports whether x is the predeclared nil constant.
func (x *operand) isNil() bool {
return x.mode == constant && x.val == nilConst
}
// isAssignable reports whether x is assignable to a variable of type T. // isAssignable reports whether x is assignable to a variable of type T.
func (x *operand) isAssignable(T Type) bool { func (x *operand) isAssignable(T Type) bool {
if x.mode == invalid || T == Typ[Invalid] { if x.mode == invalid || T == Typ[Invalid] {
...@@ -163,7 +172,7 @@ func (x *operand) isAssignable(T Type) bool { ...@@ -163,7 +172,7 @@ func (x *operand) isAssignable(T Type) bool {
// x is the predeclared identifier nil and T is a pointer, // x is the predeclared identifier nil and T is a pointer,
// function, slice, map, channel, or interface type // function, slice, map, channel, or interface type
if x.typ == Typ[UntypedNil] { if x.isNil() {
switch Tu.(type) { switch Tu.(type) {
case *Pointer, *Signature, *Slice, *Map, *Chan, *Interface: case *Pointer, *Signature, *Slice, *Map, *Chan, *Interface:
return true return true
...@@ -185,17 +194,135 @@ func (x *operand) isInteger() bool { ...@@ -185,17 +194,135 @@ func (x *operand) isInteger() bool {
x.mode == constant && isRepresentableConst(x.val, UntypedInt) x.mode == constant && isRepresentableConst(x.val, UntypedInt)
} }
// lookupField returns the struct field with the given name in typ. type lookupResult struct {
// If no such field exists, the result is nil. mode operandMode
// TODO(gri) should this be a method of Struct? typ Type
// }
func lookupField(typ *Struct, name string) *StructField {
// TODO(gri) deal with embedding and conflicts - this is // lookupFieldRecursive is similar to FieldByNameFunc in reflect/type.go
// a very basic version to get going for now. // TODO(gri): FieldByNameFunc seems more complex - what are we missing?
for _, f := range typ.Fields { func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) {
if f.Name == name { // visited records the types that have been searched already
return f visited := make(map[Type]bool)
// embedded types of the next lower level
var next []*NamedType
potentialMatch := func(mode operandMode, typ Type) bool {
if res.mode != invalid {
// name appeared multiple times at this level - annihilate
res.mode = invalid
return false
}
res.mode = mode
res.typ = typ
return true
}
// look for name in all types of this level
for len(list) > 0 {
assert(res.mode == invalid)
for _, typ := range list {
if visited[typ] {
// We have seen this type before, at a higher level.
// That higher level shadows the lower level we are
// at now, and either we would have found or not
// found the field before. Ignore this type now.
continue
}
visited[typ] = true
// look for a matching attached method
if data := typ.Obj.Data; data != nil {
if obj := data.(*ast.Scope).Lookup(name); obj != nil {
assert(obj.Type != nil)
if !potentialMatch(value, obj.Type.(Type)) {
return // name collision
}
}
}
switch typ := underlying(typ).(type) {
case *Struct:
// look for a matching fieldm and collect embedded types
for _, f := range typ.Fields {
if f.Name == name {
assert(f.Type != nil)
if !potentialMatch(variable, f.Type) {
return // name collision
}
continue
}
// Collect embedded struct fields for searching the next
// lower level, but only if we have not seen a match yet.
// Embedded fields are always of the form T or *T where
// T is a named type.
if f.IsAnonymous && res.mode == invalid {
next = append(next, deref(f.Type).(*NamedType))
}
}
case *Interface:
// look for a matching method
for _, obj := range typ.Methods {
if obj.Name == name {
assert(obj.Type != nil)
if !potentialMatch(value, obj.Type.(Type)) {
return // name collision
}
}
}
}
}
if res.mode != invalid {
// we found a match on this level
return
}
// search the next level
list = append(list[:0], next...) // don't waste underlying arrays
next = next[:0]
}
return
}
func lookupField(typ Type, name string) (operandMode, Type) {
typ = deref(typ)
if typ, ok := typ.(*NamedType); ok {
if data := typ.Obj.Data; data != nil {
if obj := data.(*ast.Scope).Lookup(name); obj != nil {
assert(obj.Type != nil)
return value, obj.Type.(Type)
}
}
}
switch typ := underlying(typ).(type) {
case *Struct:
var list []*NamedType
for _, f := range typ.Fields {
if f.Name == name {
return variable, f.Type
}
if f.IsAnonymous {
list = append(list, deref(f.Type).(*NamedType))
}
}
if len(list) > 0 {
res := lookupFieldRecursive(list, name)
return res.mode, res.typ
}
case *Interface:
for _, obj := range typ.Methods {
if obj.Name == name {
return value, obj.Type.(Type)
}
} }
} }
return nil
// not found
return invalid, nil
} }
...@@ -59,11 +59,16 @@ func isOrdered(typ Type) bool { ...@@ -59,11 +59,16 @@ func isOrdered(typ Type) bool {
return ok && t.Info&IsOrdered != 0 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 { func isComparable(typ Type) bool {
switch t := underlying(typ).(type) { switch t := underlying(typ).(type) {
case *Basic: case *Basic:
return t.Kind != Invalid return t.Kind != Invalid && t.Kind != UntypedNil
case *Pointer, *Chan, *Interface: case *Pointer, *Interface, *Chan:
// assumes types are equal for pointers and channels // assumes types are equal for pointers and channels
return true return true
case *Struct: case *Struct:
...@@ -79,6 +84,14 @@ func isComparable(typ Type) bool { ...@@ -79,6 +84,14 @@ func isComparable(typ Type) bool {
return false return false
} }
func hasNil(typ Type) bool {
switch underlying(typ).(type) {
case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
return true
}
return false
}
// identical returns true if x and y are identical. // identical returns true if x and y are identical.
func isIdentical(x, y Type) bool { func isIdentical(x, y Type) bool {
if x == y { if x == y {
......
This diff is collapsed.
...@@ -206,4 +206,10 @@ const ( ...@@ -206,4 +206,10 @@ const (
const ( const (
_b0 = iota _b0 = iota
_b1 = assert(iota + iota2 == 5) _b1 = assert(iota + iota2 == 5)
)
// special cases
const (
_n0 = nil /* ERROR "invalid constant type" */
_n1 = [ /* ERROR "not constant" */ ]int{}
) )
\ No newline at end of file
...@@ -13,3 +13,16 @@ func (T1) m /* ERROR "redeclared" */ () {} ...@@ -13,3 +13,16 @@ func (T1) m /* ERROR "redeclared" */ () {}
type T3 struct { type T3 struct {
f *T3 f *T3
} }
type T6 struct {
x int
}
func (t *T6) m1() int {
return t.x
}
func f() {
var t *T6
t.m1()
}
\ No newline at end of file
...@@ -20,7 +20,7 @@ var ( ...@@ -20,7 +20,7 @@ var (
b9 = *b0 /* ERROR "cannot indirect" */ b9 = *b0 /* ERROR "cannot indirect" */
b10 = &true /* ERROR "cannot take address" */ b10 = &true /* ERROR "cannot take address" */
b11 = &b0 b11 = &b0
b12 = <-b0 /* ERROR "not defined" */ b12 = <-b0 /* ERROR "cannot receive" */
// int // int
i0 = 1 i0 = 1
...@@ -41,7 +41,7 @@ var ( ...@@ -41,7 +41,7 @@ var (
i15 = *i0 /* ERROR "cannot indirect" */ i15 = *i0 /* ERROR "cannot indirect" */
i16 = &i0 i16 = &i0
i17 = *i16 i17 = *i16
i18 = <-i16 /* ERROR "not defined" */ i18 = <-i16 /* ERROR "cannot receive" */
// uint // uint
u0 = uint(1) u0 = uint(1)
...@@ -62,7 +62,7 @@ var ( ...@@ -62,7 +62,7 @@ var (
u15 = *u0 /* ERROR "cannot indirect" */ u15 = *u0 /* ERROR "cannot indirect" */
u16 = &u0 u16 = &u0
u17 = *u16 u17 = *u16
u18 = <-u16 /* ERROR "not defined" */ u18 = <-u16 /* ERROR "cannot receive" */
// float64 // float64
f0 = float64(1) f0 = float64(1)
...@@ -83,7 +83,7 @@ var ( ...@@ -83,7 +83,7 @@ var (
f15 = *f0 /* ERROR "cannot indirect" */ f15 = *f0 /* ERROR "cannot indirect" */
f16 = &f0 f16 = &f0
f17 = *u16 f17 = *u16
f18 = <-u16 /* ERROR "not defined" */ f18 = <-u16 /* ERROR "cannot receive" */
// complex128 // complex128
c0 = complex128(1) c0 = complex128(1)
...@@ -104,7 +104,7 @@ var ( ...@@ -104,7 +104,7 @@ var (
c15 = *c0 /* ERROR "cannot indirect" */ c15 = *c0 /* ERROR "cannot indirect" */
c16 = &c0 c16 = &c0
c17 = *u16 c17 = *u16
c18 = <-u16 /* ERROR "not defined" */ c18 = <-u16 /* ERROR "cannot receive" */
// string // string
s0 = "foo" s0 = "foo"
...@@ -115,7 +115,7 @@ var ( ...@@ -115,7 +115,7 @@ var (
s5 = *s4 /* ERROR "cannot indirect" */ s5 = *s4 /* ERROR "cannot indirect" */
s6 = &s4 s6 = &s4
s7 = *s6 s7 = *s6
s8 = <-s7 /* ERROR "not defined" */ s8 = <-s7 /* ERROR "cannot receive" */
// channel // channel
ch chan int ch chan int
...@@ -130,6 +130,6 @@ var ( ...@@ -130,6 +130,6 @@ var (
ch6 = *ch5 ch6 = *ch5
ch7 = <-ch ch7 = <-ch
ch8 = <-rc ch8 = <-rc
ch9 = <-sc /* ERROR "not defined" */ ch9 = <-sc /* ERROR "cannot receive" */
) )
\ No newline at end of file
...@@ -5,3 +5,8 @@ ...@@ -5,3 +5,8 @@
// comparisons // comparisons
package expr2 package expr2
// corner cases
var (
v0 = nil /* ERROR "cannot compare" */ == nil
)
\ No newline at end of file
...@@ -118,3 +118,17 @@ func indexes() { ...@@ -118,3 +118,17 @@ func indexes() {
_ = s[1<<30] // no compile-time error here _ = s[1<<30] // no compile-time error here
} }
type T struct {
x int
}
func (*T) m() {}
func method_expressions() {
_ = T /* ERROR "no field or method" */ .a
_ = T /* ERROR "has no method" */ .x
_ = T.m
var f func(*T) = (*T).m
var g func(*T) = ( /* ERROR "cannot assign" */ T).m
}
\ No newline at end of file
...@@ -31,6 +31,22 @@ func _() { ...@@ -31,6 +31,22 @@ func _() {
s += 1 /* ERROR "cannot convert.*string" */ s += 1 /* ERROR "cannot convert.*string" */
} }
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() { func _sends() {
var ch chan int var ch chan int
var rch <-chan int var rch <-chan int
...@@ -39,4 +55,20 @@ func _sends() { ...@@ -39,4 +55,20 @@ func _sends() {
rch /* ERROR "cannot send" */ <- x rch /* ERROR "cannot send" */ <- x
ch /* ERROR "cannot send" */ <- "foo" ch /* ERROR "cannot send" */ <- "foo"
ch <- x 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" */ :
}
} }
\ No newline at end of file
...@@ -86,8 +86,9 @@ const ( ...@@ -86,8 +86,9 @@ const (
IsString IsString
IsUntyped IsUntyped
IsOrdered = IsInteger | IsFloat | IsString IsOrdered = IsInteger | IsFloat | IsString
IsNumeric = IsInteger | IsFloat | IsComplex IsNumeric = IsInteger | IsFloat | IsComplex
IsConstType = IsBoolean | IsNumeric | IsString
) )
// A Basic represents a basic type. // A Basic represents a basic type.
...@@ -212,9 +213,8 @@ type Chan struct { ...@@ -212,9 +213,8 @@ type Chan struct {
// A NamedType represents a named type as declared in a type declaration. // A NamedType represents a named type as declared in a type declaration.
type NamedType struct { type NamedType struct {
implementsType implementsType
Obj *ast.Object // corresponding declared object Obj *ast.Object // corresponding declared object; Obj.Data.(*ast.Scope) contains methods, if any
Underlying Type // nil if not fully declared yet, never a *NamedType Underlying Type // nil if not fully declared yet; never a *NamedType
Methods ObjList // associated methods; or nil
} }
// An ObjList represents an ordered (in some fashion) list of objects. // An ObjList represents an ordered (in some fashion) list of objects.
......
...@@ -141,11 +141,11 @@ var testExprs = []testEntry{ ...@@ -141,11 +141,11 @@ var testExprs = []testEntry{
// arbitrary expressions // arbitrary expressions
dup("&x"), dup("&x"),
dup("*x"), dup("*&x"),
dup("(x)"), dup("(x)"),
dup("x + y"), dup("x + y"),
dup("x + y * 10"), dup("x + y * 10"),
dup("s.foo"), dup("t.foo"),
dup("s[0]"), dup("s[0]"),
dup("s[x:y]"), dup("s[x:y]"),
dup("s[:y]"), dup("s[:y]"),
...@@ -158,12 +158,12 @@ var testExprs = []testEntry{ ...@@ -158,12 +158,12 @@ var testExprs = []testEntry{
{"func(a, b int) []int {}()[x]", "(func literal)()[x]"}, {"func(a, b int) []int {}()[x]", "(func literal)()[x]"},
{"[]int{1, 2, 3}", "(composite literal)"}, {"[]int{1, 2, 3}", "(composite literal)"},
{"[]int{1, 2, 3}[x:]", "(composite literal)[x:]"}, {"[]int{1, 2, 3}[x:]", "(composite literal)[x:]"},
{"x.([]string)", "x.(...)"}, {"i.([]string)", "i.(...)"},
} }
func TestExprs(t *testing.T) { func TestExprs(t *testing.T) {
for _, test := range testExprs { for _, test := range testExprs {
src := "package p; var _ = " + test.src + "; var (x, y int; s []string; f func(int, float32))" src := "package p; var _ = " + test.src + "; var (x, y int; s []string; f func(int, float32) int; i interface{}; t interface { foo() })"
pkg, err := makePkg(t, src) pkg, err := makePkg(t, src)
if err != nil { if err != nil {
t.Errorf("%s: %s", src, err) t.Errorf("%s: %s", src, err)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment