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 (tc *typechecker) declGlobal(global ast.Decl) { switch d := global.(type) { case *ast.BadDecl: // ignore case *ast.GenDecl: iota := 0 var prev *ast.ValueSpec for _, spec := range d.Specs { switch s := spec.(type) { case *ast.ImportSpec: // TODO(gri) imports go into file scope case *ast.ValueSpec: switch d.Tok { case token.CONST: if s.Values == nil { // create a new spec with type and values from the previous one if prev != nil { s = &ast.ValueSpec{s.Doc, s.Names, prev.Type, prev.Values, s.Comment} } else { // TODO(gri) this should probably go into the const decl code tc.Errorf(s.Pos(), "missing initializer for const %s", s.Names[0].Name) } } for _, name := range s.Names { tc.decl(ast.Con, name, s, iota) } case token.VAR: for _, name := range s.Names { tc.decl(ast.Var, name, s, 0) } default: panic("unreachable") } prev = s iota++ case *ast.TypeSpec: obj := tc.decl(ast.Typ, s.Name, s, 0) // give all type objects an unresolved type so // that we can collect methods in the type scope typ := ast.NewType(ast.Unresolved) obj.Type = typ typ.Obj = obj default: panic("unreachable") } } case *ast.FuncDecl: if d.Recv == nil { tc.decl(ast.Fun, d.Name, d, 0) } default: panic("unreachable") } }
func ExprType(e0 ast.Expr, s *Stack) (t *ast.Type) { t = ast.NewType(ast.Basic) switch e := e0.(type) { case *ast.BasicLit: switch e.Kind { case token.STRING: t.N = ast.String default: panic(fmt.Sprintf("I don't handle basic literals such as %s", e)) } case *ast.CallExpr: switch fn := e.Fun.(type) { case *ast.Ident: switch fn.Name { case "println": return ast.NewType(ast.Tuple) case "print": return ast.NewType(ast.Tuple) default: ftype := s.Lookup(fn.Name).Type() switch ftype.N { case 0: // This is a function with no return value: easy! return ast.NewType(ast.Tuple) case 1: return ftype.Params.Objects[0].Type default: panic("I don't yet do multiple return types...") } } default: panic(fmt.Sprintf("Can't handle function of weird type %T", e.Fun)) } case *ast.Ident: return s.Lookup(e.Name).Type() default: panic(fmt.Sprintf("I can't find type of expression %s of type %T\n", e0, e0)) } return }
func TypeExpression(e ast.Expr) (t *ast.Type) { switch e := e.(type) { case *ast.Ident: switch e.Name { case "string": t = ast.NewType(ast.Basic) t.N = ast.String return default: panic("I don't understand type " + e.Name) } default: panic(fmt.Sprintf("I can't understand type expression %s of type %T\n", e, e)) } panic(fmt.Sprintf("I don't understand the type expression %s", e)) }
func init() { Universe = ast.NewScope(nil) // basic types for n, name := range ast.BasicTypes { typ := ast.NewType(ast.Basic) typ.N = n obj := ast.NewObj(ast.Typ, name) obj.Type = typ typ.Obj = obj def(obj) } // built-in functions // TODO(gri) implement this }
func (v *CompileVisitor) FunctionPrologue(fn *ast.FuncDecl) { v.Stack = v.Stack.New(fn.Name.Name) ftype := ast.NewType(ast.Function) ftype.N = uint(fn.Type.Results.NumFields()) ftype.Params = ast.NewScope(nil) fmt.Println("Working on function", fn.Name.Name) if fn.Type.Results != nil { resultnum := 0 for _, resultfield := range fn.Type.Results.List { names := []string{"_"} if resultfield.Names != nil { names = []string{} for _, i := range resultfield.Names { names = append(names, i.Name) } } t := TypeExpression(resultfield.Type) for _, n := range names { ftype.Params.Insert(&ast.Object{ast.Fun, n, t, resultfield, 0}) resultnum++ v.Stack.DefineVariable(n, t, fmt.Sprintf("return_value_%d", resultnum)) // The return values are actually allocated elsewhere... here // we just need to define the function type properly so it // gets called properly. } } } fmt.Println("Stack size after results is", v.Stack.Size) v.Stack.ReturnSize = v.Stack.Size // The arguments are pushed last argument first, so that eventually // the types of the "later" arguments can depend on the first // arguments, which seems nice to me. for pi := len(fn.Type.Params.List) - 1; pi >= 0; pi-- { paramfield := fn.Type.Params.List[pi] names := []string{"_"} if paramfield.Names != nil { names = []string{} for _, i := range paramfield.Names { names = append(names, i.Name) } } t := TypeExpression(paramfield.Type) for i := len(names) - 1; i >= 0; i-- { n := names[i] ftype.Params.Insert(&ast.Object{ast.Fun, n, t, paramfield, 0}) v.Stack.DefineVariable(n, t) // The function parameters are actually allocated // elsewhere... here we just need to define the function type // properly so it gets called properly. } } fmt.Println("Stack size after params is", v.Stack.Size) v.Stack.DefineVariable("return", IntType) fmt.Println("Stack size after return is", v.Stack.Size) v.Stack = v.Stack.New("_") DefineGlobal(fn.Name.Name, ftype) // symbol for the start name pos := myfiles.Position(fn.Pos()) v.Append(x86.Commented(x86.GlobalSymbol("main_"+fn.Name.Name), fmt.Sprint(pos.Filename, ": line ", pos.Line))) // If we had arguments, we'd want to swap them with the return // address here... }
func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Type) { x = unparen(x) // type name if t, isIdent := x.(*ast.Ident); isIdent { obj := tc.find(t) if obj.Kind != ast.Typ { tc.Errorf(t.Pos(), "%s is not a type", t.Name) if def == nil { typ = ast.NewType(ast.BadType) } else { typ = def typ.Form = ast.BadType } typ.Expr = x return } if !ref { tc.resolve(obj) // check for cycles even if type resolved } typ = obj.Type if def != nil { // new type declaration: copy type structure def.Form = typ.Form def.N = typ.N def.Key, def.Elt = typ.Key, typ.Elt def.Params = typ.Params def.Expr = x typ = def } return } // type literal typ = def if typ == nil { typ = ast.NewType(ast.BadType) } typ.Expr = x switch t := x.(type) { case *ast.SelectorExpr: if debug { fmt.Println("qualified identifier unimplemented") } typ.Form = ast.BadType case *ast.StarExpr: typ.Form = ast.Pointer typ.Elt = tc.typeFor(nil, t.X, true) case *ast.ArrayType: if t.Len != nil { typ.Form = ast.Array // TODO(gri) compute the real length // (this may call resolve recursively) (*typ).N = 42 } else { typ.Form = ast.Slice } typ.Elt = tc.typeFor(nil, t.Elt, t.Len == nil) case *ast.StructType: typ.Form = ast.Struct tc.declFields(typ.Scope, t.Fields, false) case *ast.FuncType: typ.Form = ast.Function tc.declSignature(typ, nil, t.Params, t.Results) case *ast.InterfaceType: typ.Form = ast.Interface tc.declFields(typ.Scope, t.Methods, true) case *ast.MapType: typ.Form = ast.Map typ.Key = tc.typeFor(nil, t.Key, true) typ.Elt = tc.typeFor(nil, t.Value, true) case *ast.ChanType: typ.Form = ast.Channel typ.N = uint(t.Dir) typ.Elt = tc.typeFor(nil, t.Value, true) default: if debug { fmt.Printf("x is %T\n", x) } panic("unreachable") } return }
} default: panic(fmt.Sprintf("I don't know how to pop type %s", t.Form)) } return } func SizeOnStack(t *ast.Type) (out int) { out = TypeToSize(t) if out&3 != 0 { return 4 * (out/4 + 1) } return } var IntType *ast.Type = ast.NewType(ast.Basic) var StringType *ast.Type = ast.NewType(ast.Basic) func init() { IntType.N = ast.Int StringType.N = ast.String } func PrettyType(t *ast.Type) string { switch t.Form { case ast.Tuple: out := "(" for _, o := range t.Params.Objects { if out != "(" { out += ", " }