func (tc *typechecker) resolve(obj *ast.Object) { // check for declaration cycles if tc.cyclemap[obj] { tc.Errorf(objPos(obj), "illegal cycle in declaration of %s", obj.Name) obj.Kind = ast.Bad return } tc.cyclemap[obj] = true defer func() { tc.cyclemap[obj] = false, false }() // resolve non-type objects typ := obj.Type if typ == nil { switch obj.Kind { case ast.Bad: // ignore case ast.Con: tc.declConst(obj) case ast.Var: tc.declVar(obj) //obj.Type = tc.typeFor(nil, obj.Decl.(*ast.ValueSpec).Type, false) case ast.Fun: obj.Type = ast.NewType(ast.Function) t := obj.Decl.(*ast.FuncDecl).Type tc.declSignature(obj.Type, nil, t.Params, t.Results) default: // type objects have non-nil types when resolve is called if debug { fmt.Printf("kind = %s\n", obj.Kind) } panic("unreachable") } return } // resolve type objects if typ.Form == ast.Unresolved { tc.typeFor(typ, typ.Obj.Decl.(*ast.TypeSpec).Type, false) // provide types for all methods for _, obj := range typ.Scope.Objects { if obj.Kind == ast.Fun { assert(obj.Type == nil) obj.Type = ast.NewType(ast.Method) f := obj.Decl.(*ast.FuncDecl) t := f.Type tc.declSignature(obj.Type, f.Recv, t.Params, t.Results) } } } }
func objDoc(fset *token.FileSet, pkg *ast.Package, tabIndent bool, tabWidth int, obj *ast.Object) *Doc { decl := obj.Decl kind := obj.Kind.String() tp := fset.Position(obj.Pos()) objSrc := "" pkgName := "" if pkg != nil && pkg.Name != "builtin" { pkgName = pkg.Name } if obj.Kind == ast.Pkg { pkgName = "" doc := "" // special-case `package name` is generated as a TypeSpec if v, ok := obj.Decl.(*ast.TypeSpec); ok && v.Doc != nil { doc = "/*\n" + v.Doc.Text() + "\n*/\n" } objSrc = doc + "package " + obj.Name } else if af, ok := pkg.Files[tp.Filename]; ok { switch decl.(type) { case *ast.TypeSpec, *ast.ValueSpec, *ast.Field: line := tp.Line - 1 for _, cg := range af.Comments { cgp := fset.Position(cg.End()) if cgp.Filename == tp.Filename && cgp.Line == line { switch v := decl.(type) { case *ast.TypeSpec: v.Doc = cg case *ast.ValueSpec: v.Doc = cg case *ast.Field: pkgName = "" kind = "field" } break } } } } if objSrc == "" { objSrc, _ = printSrc(fset, decl, tabIndent, tabWidth) } return &Doc{ Src: objSrc, Pkg: pkgName, Name: obj.Name, Kind: kind, Fn: tp.Filename, Row: tp.Line - 1, Col: tp.Column - 1, } }
// checkObj type checks an object. func (c *checker) checkObj(obj *ast.Object, ref bool) { if obj.Type != nil { // object has already been type checked return } switch obj.Kind { case ast.Bad: // ignore case ast.Con: // TODO(gri) complete this case ast.Typ: typ := &Name{Obj: obj} obj.Type = typ // "mark" object so recursion terminates typ.Underlying = Underlying(c.makeType(obj.Decl.(*ast.TypeSpec).Type, ref)) case ast.Var: // TODO(gri) complete this case ast.Fun: // TODO(gri) complete this default: panic("unreachable") } }
func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (params ObjList, isVariadic bool) { if list == nil { return } var last *ast.Object for i, field := range list.List { ftype := field.Type if t, _ := ftype.(*ast.Ellipsis); t != nil { ftype = t.Elt if variadicOk && i == len(list.List)-1 { isVariadic = true } else { check.invalidAST(field.Pos(), "... not permitted") // ok to continue } } // the parser ensures that f.Tag is nil and we don't // care if a constructed AST contains a non-nil tag typ := check.typ(ftype, true) if len(field.Names) > 0 { // named parameter for _, name := range field.Names { obj := name.Obj obj.Type = typ params = append(params, obj) last = obj } } else { // anonymous parameter obj := ast.NewObj(ast.Var, "") obj.Type = typ params = append(params, obj) last = obj } } // For a variadic function, change the last parameter's object type // from T to []T (this is the type used inside the function), but // keep a copy of the object with the original type T in the params // list (this is the externally visible type). if isVariadic { // if isVariadic is set, last must exist and len(params) > 0 copy := *last last.Type = &Slice{Elt: last.Type.(Type)} params[len(params)-1] = © } return }
// 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 object (or nil if error) var obj *ast.Object if ident, ok := typ.(*ast.Ident); ok && ident.Obj != nil { obj = ident.Obj if obj.Kind != ast.Typ { check.errorf(ident.Pos(), "%s is not a type", ident.Name) obj = nil } // TODO(gri) determine if obj was defined in this package /* if check.notLocal(obj) { check.errorf(ident.Pos(), "cannot define methods on non-local type %s", ident.Name) obj = nil } */ } else { // If it's not an identifier or the identifier wasn't declared/resolved, // the parser/resolver already reported an error. Nothing to do here. } // determine base type scope (or nil if error) var scope *ast.Scope if obj != nil { if obj.Data != nil { scope = obj.Data.(*ast.Scope) } else { scope = ast.NewScope(nil) obj.Data = scope } } else { // use a dummy scope so that meth can be declared in // presence of an error and get an associated object // (always use a new scope so that we don't get double // declaration errors) scope = ast.NewScope(nil) } check.declare(scope, ast.Fun, meth.Name, meth) }
// checkObj type checks an object. func (c *checker) checkObj(obj *ast.Object, ref bool) { if obj.Type != nil { // object has already been type checked return } switch obj.Kind { case ast.Bad: // ignore case ast.Con: // TODO(gri) complete this case ast.Typ: typ := &Name{Obj: obj} obj.Type = typ // "mark" object so recursion terminates typ.Underlying = Underlying(c.makeType(obj.Decl.(*ast.TypeSpec).Type, ref)) case ast.Var: // TODO(gri) complete this case ast.Fun: fdecl := obj.Decl.(*ast.FuncDecl) ftyp := c.makeType(fdecl.Type, ref).(*Func) obj.Type = ftyp if fdecl.Recv != nil { recvField := fdecl.Recv.List[0] if len(recvField.Names) > 0 { ftyp.Recv = recvField.Names[0].Obj } else { ftyp.Recv = ast.NewObj(ast.Var, "_") ftyp.Recv.Decl = recvField } c.checkObj(ftyp.Recv, ref) // TODO(axw) add method to a list in the receiver type. } // TODO(axw) check function body, if non-nil. default: panic("unreachable") } }
func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident, typ ast.Expr, rhs []ast.Expr, 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 t Type if typ != nil { t = check.typ(typ, false) } // len(lhs) > 0 if len(lhs) == len(rhs) { // check only lhs and rhs corresponding to obj var l, r ast.Expr for i, name := range lhs { if name.Obj == obj { l = lhs[i] r = rhs[i] break } } assert(l != nil) obj.Type = t check.assign1to1(l, r, nil, true, iota) 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 { for _, name := range lhs { name.Obj.Type = t } } // 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) } }
// 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 object var obj *ast.Object if ident, ok := typ.(*ast.Ident); ok && ident.Obj != nil { obj = ident.Obj if obj.Kind != ast.Typ { check.errorf(ident.Pos(), "%s is not a type", ident.Name) return // ignore this method } // TODO(gri) determine if obj was defined in this package /* if check.notLocal(obj) { check.errorf(ident.Pos(), "cannot define methods on non-local type %s", ident.Name) return // ignore this method } */ } else { // If it's not an identifier or the identifier wasn't declared/resolved, // the parser/resolver already reported an error. Nothing to do here. return // ignore this method } // declare method in receiver base type scope var scope *ast.Scope if obj.Data != nil { scope = obj.Data.(*ast.Scope) } else { scope = ast.NewScope(nil) obj.Data = scope } check.declare(scope, ast.Fun, meth.Name, meth) }
func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident, typ ast.Expr, rhs []ast.Expr, iota int) { if len(lhs) == 0 { check.invalidAST(pos, "missing lhs in declaration") return } var t Type if typ != nil { t = check.typ(typ, false) } // len(lhs) >= 1 if len(lhs) == len(rhs) { // check only corresponding lhs and rhs var l, r ast.Expr for i, ident := range lhs { if ident.Obj == obj { l = lhs[i] r = rhs[i] break } } assert(l != nil) obj.Type = t // check rhs var x operand check.expr(&x, r, t, iota) // assign to lhs check.assignment(l, &x, true) return } if t != nil { for _, name := range lhs { name.Obj.Type = t } } // 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) } }
// convertUntyped takes an object, and, if it is untyped, gives it // a named builtin type: bool, rune, int, float64, complex128 or string. func maybeConvertUntyped(obj *ast.Object) bool { switch obj.Type { case Bool.Underlying: obj.Type = Bool case Rune.Underlying: obj.Type = Rune case Int.Underlying: obj.Type = Int case Float64.Underlying: obj.Type = Float64 case Complex128.Underlying: obj.Type = Complex128 case String.Underlying: obj.Type = String default: return false } return true }
// obj type checks an object. func (check *checker) obj(obj *ast.Object, cycleOk bool) { if trace { fmt.Printf("obj(%s)\n", obj.Name) } if obj.Type != nil { // object has already been type checked return } switch obj.Kind { case ast.Bad, ast.Pkg: // nothing to do case ast.Con: if obj.Data == nil { check.errorf(obj.Pos(), "illegal cycle in initialization of %s", obj.Name) return } spec, ok := obj.Decl.(*ast.ValueSpec) assert(ok) // The Data stored with the constant is the value of iota for that // ast.ValueSpec. Use it for the evaluation of the initialization // expressions. iota := obj.Data.(int) obj.Data = nil check.decl(spec.Pos(), obj, spec.Names, spec.Type, check.specValues(spec), iota) case ast.Var: // TODO(gri) missing cycle detection spec, ok := obj.Decl.(*ast.ValueSpec) if !ok { // TODO(gri) the assertion fails for "x, y := 1, 2, 3" it seems fmt.Printf("var = %s\n", obj.Name) } assert(ok) check.decl(spec.Pos(), obj, spec.Names, spec.Type, spec.Values, 0) case ast.Typ: typ := &NamedType{Obj: obj} obj.Type = typ // "mark" object so recursion terminates typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk)) // collect associated methods, if any if obj.Data != nil { scope := obj.Data.(*ast.Scope) // struct fields must not conflict with methods if t, ok := typ.Underlying.(*Struct); ok { for _, f := range t.Fields { 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) } } } // collect methods methods := make(ObjList, len(scope.Objects)) i := 0 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 check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name) } } } case ast.Fun: fdecl := obj.Decl.(*ast.FuncDecl) ftyp := check.typ(fdecl.Type, cycleOk).(*Signature) obj.Type = ftyp if fdecl.Recv != nil { // TODO(gri) handle method receiver } check.stmt(fdecl.Body) default: panic("unreachable") } }
func (c *compiler) VisitValueSpec(valspec *ast.ValueSpec, isconst bool) { // Check if the value-spec has already been visited (referenced // before definition visited.) if len(valspec.Names) > 0 { if _, ok := valspec.Names[0].Obj.Data.(Value); ok { return } } var iotaObj *ast.Object = types.Universe.Lookup("iota") defer func(data interface{}) { iotaObj.Data = data }(iotaObj.Data) pkgname, ispackagelevel := c.pkgmap[valspec.Names[0].Obj] if ispackagelevel && !isconst { c.createGlobals(valspec.Names, valspec.Values, pkgname) return } var values []Value if len(valspec.Values) == 1 && len(valspec.Names) > 1 { values = c.destructureExpr(valspec.Values[0]) } else if len(valspec.Values) > 0 { values = make([]Value, len(valspec.Names)) for i, name_ := range valspec.Names { if isconst { if iota_, isint := (name_.Obj.Data).(int); isint { iotaValue := c.NewConstValue(token.INT, strconv.Itoa(iota_)) iotaObj.Data = iotaValue } } values[i] = c.VisitExpr(valspec.Values[i]) } } for i, name := range valspec.Names { if name.Name == "_" { continue } // For constants, we just pass the ConstValue around. Otherwise, we // will convert it to an LLVMValue. var value Value if isconst { value = values[i].Convert(name.Obj.Type.(types.Type)) } else { // The variable should be allocated on the stack if it's // declared inside a function. var llvmInit llvm.Value typ := name.Obj.Type.(types.Type) ptr := c.builder.CreateAlloca(c.types.ToLLVM(typ), name.Name) if values == nil || values[i] == nil { // If no initialiser was specified, set it to the // zero value. llvmInit = llvm.ConstNull(c.types.ToLLVM(typ)) } else { llvmInit = values[i].Convert(typ).LLVMValue() } c.builder.CreateStore(llvmInit, ptr) value = c.NewLLVMValue(ptr, &types.Pointer{Base: typ}).makePointee() } name.Obj.Data = value } }
func check(fset *token.FileSet, pkg *ast.Package, types map[ast.Expr]Type) error { var check checker check.fset = fset check.pkg = pkg check.types = types // Compute sorted list of file names so that // package file iterations are reproducible (needed for testing). filenames := make([]string, len(pkg.Files)) { i := 0 for filename := range pkg.Files { filenames[i] = filename i++ } sort.Strings(filenames) } // Associate methods with types // TODO(gri) All other objects are resolved by the parser. // Consider doing this in the parser (and provide the info // in the AST. In the long-term (might require Go 1 API // changes) it's probably easier to do all the resolution // in one place in the type checker. See also comment // with checker.declare. for _, filename := range filenames { file := pkg.Files[filename] for _, decl := range file.Decls { if meth, ok := decl.(*ast.FuncDecl); ok && meth.Recv != nil { // 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 object (or nil if error) var obj *ast.Object if ident, ok := typ.(*ast.Ident); ok && ident.Obj != nil { obj = ident.Obj if obj.Kind != ast.Typ { check.errorf(ident.Pos(), "%s is not a type", ident.Name) obj = nil } // TODO(gri) determine if obj was defined in this package /* if check.notLocal(obj) { check.errorf(ident.Pos(), "cannot define methods on non-local type %s", ident.Name) obj = nil } */ } else { // If it's not an identifier or the identifier wasn't declared/resolved, // the parser/resolver already reported an error. Nothing to do here. } // determine base type scope (or nil if error) var scope *ast.Scope if obj != nil { if obj.Data != nil { scope = obj.Data.(*ast.Scope) } else { scope = ast.NewScope(nil) obj.Data = scope } } else { // use a dummy scope so that meth can be declared in // presence of an error and get an associated object // (always use a new scope so that we don't get double // declaration errors) scope = ast.NewScope(nil) } check.declare(scope, ast.Fun, meth.Name, meth) } } } // Sort objects so that we get reproducible error // positions (this is only needed for testing). // TODO(gri): Consider ast.Scope implementation that // provides both a list and a map for fast lookup. // Would permit the use of scopes instead of ObjMaps // elsewhere. list := make(ObjList, len(pkg.Scope.Objects)) { i := 0 for _, obj := range pkg.Scope.Objects { list[i] = obj i++ } list.Sort() } // Check global objects. for _, obj := range list { check.obj(obj, false) } // TODO(gri) Missing pieces: // - blank (_) objects and init functions are not in scopes but should be type-checked // do not remove multiple errors per line - depending on // order or error reporting this may hide the real error return check.errors.Err() }
// object typechecks an object by assigning it a type; obj.Type must be nil. // Callers must check obj.Type before calling object; this eliminates a call // 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 { case ast.Bad, ast.Pkg: // nothing to do case ast.Con, ast.Var: // The obj.Data field for constants and variables is initialized // to the respective (hypothetical, for variables) iota value by // the parser. The object's fields can be in one of the following // states: // Type != nil => the constant value is Data // Type == nil => the object is not typechecked yet, and Data can be: // Data is int => Data is the value of iota for this declaration // Data == nil => the object's expression is being evaluated if obj.Data == nil { check.errorf(obj.Pos(), "illegal cycle in initialization of %s", obj.Name) obj.Type = Typ[Invalid] return } spec := obj.Decl.(*ast.ValueSpec) iota := obj.Data.(int) obj.Data = nil // determine initialization expressions values := spec.Values if len(values) == 0 && obj.Kind == ast.Con { values = check.initexprs[spec] } check.valueSpec(spec.Pos(), obj, spec.Names, spec.Type, values, iota) case ast.Typ: typ := &NamedType{Obj: obj} obj.Type = typ // "mark" object so recursion terminates typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk)) // typecheck associated method signatures if obj.Data != nil { scope := obj.Data.(*ast.Scope) 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.Pos(), "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.Objects { 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) // ok to continue } } // typecheck method signatures for _, obj := range scope.Objects { mdecl := obj.Decl.(*ast.FuncDecl) sig := check.typ(mdecl.Type, cycleOk).(*Signature) params, _ := check.collectParams(mdecl.Recv, false) sig.Recv = params[0] // the parser/assocMethod ensure there is exactly one parameter obj.Type = sig check.later(obj, sig, mdecl.Body) } } case ast.Fun: fdecl := obj.Decl.(*ast.FuncDecl) // 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: panic("unreachable") } }
func (c *compiler) Resolve(obj *ast.Object) Value { if obj.Kind == ast.Pkg { return nil } else if obj.Kind == ast.Typ { return TypeValue{obj.Type.(types.Type)} } value, isvalue := (obj.Data).(Value) if isvalue { return value } switch obj.Kind { case ast.Con: if obj.Decl != nil { valspec := obj.Decl.(*ast.ValueSpec) c.VisitValueSpec(valspec, true) value = (obj.Data).(Value) } else if obj == types.Nil { return NilValue{c} } else { var typ types.Type switch x := obj.Type.(type) { case *types.Basic: typ = x case *types.Name: typ = x.Underlying.(*types.Basic) default: panic(fmt.Sprintf("unreachable (%T)", x)) } value = ConstValue{(obj.Data.(types.Const)), c, typ} obj.Data = value } case ast.Fun: var funcdecl *ast.FuncDecl if obj.Decl != nil { funcdecl = obj.Decl.(*ast.FuncDecl) } else { funcdecl = &ast.FuncDecl{ Name: &ast.Ident{Name: obj.Name, Obj: obj}, } } value = c.VisitFuncProtoDecl(funcdecl) obj.Data = value case ast.Var: switch x := (obj.Decl).(type) { case *ast.ValueSpec: c.VisitValueSpec(x, false) case *ast.Field: // No-op. Fields will be yielded for function // arg/recv/ret. We update the .Data field of the // object when we enter the function definition. if obj.Data == nil { panic("expected obj.Data value") } } // If it's an external variable, we'll need to create a global // value reference here. if obj.Data == nil { module := c.module.Module t := obj.Type.(types.Type) name := c.pkgmap[obj] + "." + obj.Name g := llvm.AddGlobal(module, c.types.ToLLVM(t), name) g.SetLinkage(llvm.AvailableExternallyLinkage) obj.Data = c.NewLLVMValue(g, t) } value = (obj.Data).(Value) } return value }
func (c *compiler) Resolve(obj *ast.Object) Value { if obj.Kind == ast.Pkg { return nil } else if obj.Kind == ast.Typ { return TypeValue{obj.Type.(types.Type)} } value, isvalue := (obj.Data).(Value) if isvalue { return value } switch obj.Kind { case ast.Con: if obj.Decl != nil { valspec := obj.Decl.(*ast.ValueSpec) c.VisitValueSpec(valspec, true) value = (obj.Data).(Value) } else if obj == types.Nil { return NilValue{c} } else { typ := obj.Type.(types.Type) value = ConstValue{(obj.Data.(types.Const)), c, typ} obj.Data = value } case ast.Fun: var funcdecl *ast.FuncDecl if obj.Decl != nil { funcdecl = obj.Decl.(*ast.FuncDecl) } else { funcdecl = &ast.FuncDecl{ Name: &ast.Ident{Name: obj.Name, Obj: obj}, } } value = c.VisitFuncProtoDecl(funcdecl) obj.Data = value case ast.Var: switch x := (obj.Decl).(type) { case *ast.ValueSpec: c.VisitValueSpec(x, false) case *ast.Field: // No-op. Fields will be yielded for function // arg/recv/ret. We update the .Data field of the // object when we enter the function definition. if obj.Data == nil { panic("expected obj.Data value") } } // If it's an external variable, we'll need to create a global // value reference here. It may be possible for multiple objects // to refer to the same variable. if obj.Data == nil { module := c.module.Module t := obj.Type.(types.Type) name := c.pkgmap[obj] + "." + obj.Name g := module.NamedGlobal(name) if g.IsNil() { g = llvm.AddGlobal(module, c.types.ToLLVM(t), name) } obj.Data = c.NewLLVMValue(g, &types.Pointer{Base: t}).makePointee() } value = (obj.Data).(Value) } return value }
// object typechecks an object by assigning it a type; obj.Type must be nil. // Callers must check obj.Type before calling object; this eliminates a call // 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 { case ast.Bad, ast.Pkg: // nothing to do case ast.Con, ast.Var: // The obj.Data field for constants and variables is initialized // to the respective (hypothetical, for variables) iota value by // the parser. The object's fields can be in one of the following // states: // Type != nil => the constant value is Data // Type == nil => the object is not typechecked yet, and Data can be: // Data is int => Data is the value of iota for this declaration // Data == nil => the object's expression is being evaluated if obj.Data == nil { check.errorf(obj.Pos(), "illegal cycle in initialization of %s", obj.Name) obj.Type = Typ[Invalid] return } spec := obj.Decl.(*ast.ValueSpec) iota := obj.Data.(int) obj.Data = nil // determine initialization expressions values := spec.Values if len(values) == 0 && obj.Kind == ast.Con { values = check.initexprs[spec] } check.valueSpec(spec.Pos(), obj, spec.Names, spec.Type, values, iota) case ast.Typ: typ := &NamedType{Obj: obj} obj.Type = typ // "mark" object so recursion terminates typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk)) // typecheck associated method signatures if obj.Data != nil { scope := obj.Data.(*ast.Scope) 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.Pos(), "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.Objects { 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) } // 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: fdecl := obj.Decl.(*ast.FuncDecl) if fdecl.Recv != nil { // This will ensure that the method base type is // type-checked check.collectFields(token.FUNC, fdecl.Recv, true) } ftyp := check.typ(fdecl.Type, cycleOk).(*Signature) obj.Type = ftyp check.function(ftyp, fdecl.Body) default: panic("unreachable") } }
func (c *compiler) VisitValueSpec(valspec *ast.ValueSpec, isconst bool) { var value_type types.Type if valspec.Type != nil { value_type = c.GetType(valspec.Type) } var iota_obj *ast.Object = types.Universe.Lookup("iota") defer func(data interface{}) { iota_obj.Data = data }(iota_obj.Data) for i, name_ := range valspec.Names { // We may resolve constants in the process of resolving others. obj := name_.Obj if _, isvalue := (obj.Data).(Value); isvalue { continue } // Set iota if necessary. if isconst { if iota_, isint := (name_.Obj.Data).(int); isint { iota_value := c.NewConstValue(token.INT, strconv.Itoa(iota_)) iota_obj.Data = iota_value // Con objects with an iota have an embedded ValueSpec // in the Decl field. We'll just pull it out and use it // for evaluating the expression below. valspec = (name_.Obj.Decl).(*ast.ValueSpec) } } // Expression may have side-effects, so compute it regardless of // whether it'll be assigned to a name. var expr ast.Expr if i < len(valspec.Values) && valspec.Values[i] != nil { expr = valspec.Values[i] } // For constants, we just pass the ConstValue around. Otherwise, we // will convert it to an LLVMValue. var value Value name := name_.String() if !isconst { ispackagelevel := len(c.functions) == 0 if !ispackagelevel { // Visit the expression. var init_ Value if expr != nil { init_ = c.VisitExpr(expr) if value_type == nil { value_type = init_.Type() } } // The variable should be allocated on the stack if it's // declared inside a function. var llvm_init llvm.Value stack_value := c.builder.CreateAlloca( c.types.ToLLVM(value_type), name) if init_ == nil { // If no initialiser was specified, set it to the // zero value. llvm_init = llvm.ConstNull(c.types.ToLLVM(value_type)) } else { llvm_init = init_.Convert(value_type).LLVMValue() } c.builder.CreateStore(llvm_init, stack_value) llvm_value := c.NewLLVMValue(stack_value, &types.Pointer{Base: value_type}) value = llvm_value.makePointee() } else { // ispackagelevel // Set the initialiser. If it's a non-const value, then // we'll have to do the assignment in a global constructor // function. export := name_.IsExported() value = c.createGlobal(expr, value_type, name, export) } } else { // isconst value = c.VisitExpr(expr).(ConstValue) if value_type != nil { value = value.Convert(value_type) } } if name != "_" { obj.Data = value } } }
// checkObj type checks an object. func (c *checker) checkObj(obj *ast.Object, ref bool) { if obj.Type != nil { // object has already been type checked return } switch obj.Kind { case ast.Bad: // ignore case ast.Con: valspec := obj.Decl.(*ast.ValueSpec) if valspec.Type != nil { obj.Type = c.makeType(valspec.Type, ref) for _, name := range valspec.Names { name.Obj.Type = obj.Type } } if valspec.Values != nil { oldData := Iota.Data for i, name := range valspec.Names { if name.Obj != nil { c.checkExpr(valspec.Values[i], []*ast.Ident{name}) iotaValue := name.Obj.Data.(int) Iota.Data = Const{big.NewInt(int64(iotaValue))} constValue := c.evalConst(valspec.Values[i]) typ := name.Obj.Type.(Type) name.Obj.Data = constValue.Convert(&typ) } else { c.checkExpr(valspec.Values[i], nil) } } if oldData != nil { Iota.Data = oldData } } case ast.Typ: typ := &Name{Package: c.pkgid, Obj: obj} obj.Type = typ // "mark" object so recursion terminates typ.Underlying = Underlying(c.makeType(obj.Decl.(*ast.TypeSpec).Type, ref)) if methobjs := c.methods[obj]; methobjs != nil { methobjs.Sort() typ.Methods = methobjs // Check for instances of field and method with same name. if s, ok := typ.Underlying.(*Struct); ok { for _, m := range methobjs { if _, ok := s.FieldIndices[m.Name]; ok { c.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, m.Name) } } } // 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 methobjs { recv := m.Decl.(*ast.FuncDecl).Recv.List[0].Type c.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name) } } } for _, m := range typ.Methods { c.checkObj(m, ref) } case ast.Var: var names []*ast.Ident var values []ast.Expr var typexpr ast.Expr switch x := obj.Decl.(type) { case *ast.ValueSpec: names = x.Names values = x.Values typexpr = x.Type case *ast.Field: names = x.Names typexpr = x.Type case *ast.AssignStmt: c.checkStmt(x) if obj.Type == nil { panic("obj.Type == nil") } default: panic(fmt.Sprintf("unimplemented (%T)", x)) } if names != nil { // nil for anonymous field var typ Type if typexpr != nil { typ = c.makeType(typexpr, ref) for i, name := range names { if name.Obj != nil { name.Obj.Type = typ } else { names[i] = nil } } } if len(values) == 1 && len(names) > 1 { // multi-value assignment c.checkExpr(values[0], names) } else if len(values) == len(names) { for i, name := range names { if name.Obj != nil { c.checkExpr(values[i], []*ast.Ident{name}) maybeConvertUntyped(name.Obj) } else { c.checkExpr(values[i], nil) } } } } case ast.Fun: fndecl := obj.Decl.(*ast.FuncDecl) obj.Type = c.makeType(fndecl.Type, ref) fn := obj.Type.(*Func) if fndecl.Recv != nil { recvField := fndecl.Recv.List[0] names := recvField.Names if len(recvField.Names) > 0 { fn.Recv = recvField.Names[0].Obj } else { fn.Recv = ast.NewObj(ast.Var, "_") fn.Recv.Decl = recvField name := &ast.Ident{Name: "_", Obj: fn.Recv} recvField.Names = []*ast.Ident{name} } c.checkObj(fn.Recv, ref) recvField.Names = names } else { // Only check body of non-method functions. We check method // bodies later, to avoid references to incomplete types. c.checkFunc(fndecl.Body, fn) } default: panic("unreachable") } }
// stmt typechecks statement s. func (check *checker) stmt(s ast.Stmt) { switch s := s.(type) { case *ast.BadStmt, *ast.EmptyStmt: // ignore case *ast.DeclStmt: check.decl(s.Decl) 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 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 || !x.isAssignable(tch.Elt) { 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, y operand check.expr(&x, s.X, nil, -1) check.expr(&y, &ast.BasicLit{ValuePos: x.pos(), Kind: token.INT, Value: "1"}, nil, -1) // use x's position check.binary(&x, &y, op, nil) 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, y operand check.expr(&x, s.Lhs[0], nil, -1) check.expr(&y, s.Rhs[0], nil, -1) check.binary(&x, &y, op, nil) 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.functypes[len(check.functypes)-1] 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 parameter! named = true } name := ast.NewIdent(res.Name) name.NamePos = s.Pos() name.Obj = res 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: unimplemented() 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 !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 if s.Tag != nil { check.expr(&x, s.Tag, nil, -1) } else { // TODO(gri) should provide a position (see IncDec) for good error messages x.mode = constant x.typ = Typ[UntypedBool] x.val = true } check.multipleDefaults(s.Body.List) for _, s := range s.Body.List { clause, _ := s.(*ast.CaseClause) if clause == nil { continue // error reported before } for _, expr := range clause.List { var y operand check.expr(&y, expr, nil, -1) // TODO(gri) x and y must be comparable } 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 *ast.Object // lhs identifier object 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 = ident.Obj 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 to nil when we are done so // that the type cannot be used by mistake. if lhs != nil { lhs.Type = nil } 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 !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). if s.Key != nil { x.typ = 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 check.assign1to1(s.Value, nil, &x, decl, -1) } check.stmt(s.Body) default: check.errorf(s.Pos(), "invalid statement") } }
// checkObj type checks an object. func (c *checker) checkObj(obj *ast.Object, ref bool) { if obj.Type != nil { // object has already been type checked return } switch obj.Kind { case ast.Bad: // ignore case ast.Con: valspec := obj.Decl.(*ast.ValueSpec) if valspec.Type != nil { obj.Type = c.makeType(valspec.Type, ref) for _, name := range valspec.Names { name.Obj.Type = obj.Type } } if valspec.Values != nil { for i, name := range valspec.Names { if name.Obj != nil { c.checkExpr(valspec.Values[i], []*ast.Ident{name}) } else { c.checkExpr(valspec.Values[i], nil) } } } case ast.Typ: typ := &Name{Obj: obj} obj.Type = typ // "mark" object so recursion terminates if methobjs := c.methods[obj]; methobjs != nil { methobjs.Sort() typ.Methods = methobjs } typ.Underlying = Underlying(c.makeType(obj.Decl.(*ast.TypeSpec).Type, ref)) for _, m := range typ.Methods { c.checkObj(m, ref) } case ast.Var: var names []*ast.Ident var values []ast.Expr var typexpr ast.Expr switch x := obj.Decl.(type) { case *ast.ValueSpec: names = x.Names values = x.Values typexpr = x.Type case *ast.Field: names = x.Names typexpr = x.Type case *ast.AssignStmt: c.checkStmt(x) if obj.Type == nil { panic("obj.Type == nil") } default: panic(fmt.Sprintf("unimplemented (%T)", x)) } if names != nil { // nil for anonymous field var typ Type if typexpr != nil { typ = c.makeType(typexpr, ref) for i, name := range names { if name.Obj != nil { name.Obj.Type = typ } else { names[i] = nil } } } if len(values) == 1 && len(names) > 1 { // multi-value assignment c.checkExpr(values[0], names) } else if len(values) == len(names) { for i, name := range names { if name.Obj != nil { c.checkExpr(values[i], []*ast.Ident{name}) } else { c.checkExpr(values[i], nil) } } } } case ast.Fun: fndecl := obj.Decl.(*ast.FuncDecl) obj.Type = c.makeType(fndecl.Type, ref) fn := obj.Type.(*Func) if fndecl.Recv != nil { recvField := fndecl.Recv.List[0] if len(recvField.Names) > 0 { fn.Recv = recvField.Names[0].Obj } else { fn.Recv = ast.NewObj(ast.Var, "_") fn.Recv.Decl = recvField } c.checkObj(fn.Recv, ref) } else { // Only check body of non-method functions. We check method // bodies later, to avoid references to incomplete types. if fndecl.Body != nil { c.checkStmt(fndecl.Body) } } default: panic("unreachable") } }