// tysubst attempts to substitute all type variables within a single return // type with their corresponding Go type from the type environment. // // tysubst will panic if a type variable is unbound, or if it encounters a // type that cannot be dynamically created. Such types include arrays, // functions and structs. (A limitation of the `reflect` package.) func (rt returnType) tysubst(typ reflect.Type) reflect.Type { if tyname := tyvarName(typ); len(tyname) > 0 { if thetype, ok := rt.tyenv[tyname]; !ok { rt.panic("Unbound type variable %s.", tyname) } else { return thetype } } switch typ.Kind() { case reflect.Array: rt.panic("Cannot dynamically create Array types.") case reflect.Chan: return reflect.ChanOf(typ.ChanDir(), rt.tysubst(typ.Elem())) case reflect.Func: rt.panic("Cannot dynamically create Function types.") case reflect.Interface: rt.panic("TODO") case reflect.Map: return reflect.MapOf(rt.tysubst(typ.Key()), rt.tysubst(typ.Elem())) case reflect.Ptr: return reflect.PtrTo(rt.tysubst(typ.Elem())) case reflect.Slice: return reflect.SliceOf(rt.tysubst(typ.Elem())) case reflect.Struct: rt.panic("Cannot dynamically create Struct types.") case reflect.UnsafePointer: rt.panic("Cannot dynamically create unsafe.Pointer types.") } // We've covered all the composite types, so we're only left with // base types. return typ }
// Return the string that should be used to refer to the supplied type within // the given package. The output is not guaranteed to be pretty, and should be // run through a tool like gofmt afterward. // // For example, a pointer to an io.Reader may be rendered as "*Reader" or // "*io.Reader" depending on whether the package path is "io" or not. func typeString( t reflect.Type, pkgPath string) (s string) { // Is this type named? If so we use its name, possibly with a package prefix. // // Examples: // // int // string // error // gcs.Bucket // if t.Name() != "" { if t.PkgPath() == pkgPath { s = t.Name() } else { s = t.String() } return } // This type is unnamed. Recurse. switch t.Kind() { case reflect.Array: s = fmt.Sprintf("[%d]%s", t.Len(), typeString(t.Elem(), pkgPath)) case reflect.Chan: s = fmt.Sprintf("%s %s", t.ChanDir(), typeString(t.Elem(), pkgPath)) case reflect.Func: s = typeString_Func(t, pkgPath) case reflect.Interface: s = typeString_Interface(t, pkgPath) case reflect.Map: s = fmt.Sprintf( "map[%s]%s", typeString(t.Key(), pkgPath), typeString(t.Elem(), pkgPath)) case reflect.Ptr: s = fmt.Sprintf("*%s", typeString(t.Elem(), pkgPath)) case reflect.Slice: s = fmt.Sprintf("[]%s", typeString(t.Elem(), pkgPath)) case reflect.Struct: s = typeString_Struct(t, pkgPath) default: log.Panicf("Unhandled kind %v for type: %v", t.Kind(), t) } return }
func ValType(t reflect.Type) (decl string) { switch k := t.Kind(); k { case reflect.Struct: decl = "struct {\n" for i, ed := 0, t.NumField(); i < ed; i++ { ft := t.Field(i) if ft.Tag != "-" || ft.Tag.Get("goval") == "-" { s := ft.Name + " " + ValType(ft.Type) if ft.Tag != "" { s += " `" + strings.Replace("`", "\\`", string(ft.Tag), -1) + "`" } decl += indent(s) + "\n" } } decl += "}" case reflect.Array: decl = "[" + strconv.Itoa(t.Len()) + "]" + Val(t.Elem()) case reflect.Slice: decl = "[]" + Val(t.Elem()) case reflect.Chan: switch t.ChanDir() { case reflect.RecvDir: decl = "<-chan " case reflect.SendDir: decl = "chan<- " case reflect.BothDir: decl = "chan " default: panic("Didn't expect a dir other than send, recieve or both.") } decl += Val(t.Elem()) case reflect.Map: decl = "map[" + ValType(t.Key()) + "]" + ValType(t.Elem()) case reflect.Ptr: decl = "*" + ValType(t.Elem()) case reflect.Interface: decl = "interface {\n" for i, ed := 0, t.NumMethod(); i < ed; i++ { ft := t.Method(i) s := ft.Name + FormatFuncArguments(ft.Type) decl += indent(s) + "\n" } decl += "}" case reflect.Func: decl = "func" + FormatFuncArguments(t) default: return k.String() } return }
// checkMethod verifies whether the specified method has a valid RPC handler // signature: // func(R, Context, IN, chan<- OUT) error // func(R, Context, IN) (OUT, error) // // If so, returns a filled-in *Method; otherwise returns an error. func checkMethod(rcvr interface{}, m reflect.Method) (*Method, error) { if !ast.IsExported(m.Name) { return nil, errors.New("method is not exported") } t := m.Type if t.Kind() != reflect.Func || t.NumIn() < 3 || t.In(1) != ctxType || !structOrStructPtr(t.In(2)) { return nil, errors.New("invalid method signature") } var ( stream bool out reflect.Type ) switch { case t.NumIn() == 4 && t.NumOut() == 1 && t.Out(0) == errType: // Signature: (receiver, Context, input, chan output) => error out = t.In(3) if out.Kind() != reflect.Chan || out.ChanDir()&reflect.SendDir == 0 { return nil, errors.New("invalid output type") } stream = true out = out.Elem() case t.NumIn() == 3 && t.NumOut() == 2 && t.Out(1) == errType: // Signature: (receiver, Context, input) => (output, error) out = t.Out(0) } in := t.In(2) if in.Kind() == reflect.Ptr { in = in.Elem() } params := []string{} for i := 0; i < in.NumField(); i++ { params = append(params, in.Field(i).Name) } sort.Strings(params) return &Method{ Name: m.Name, Params: params, Stream: stream, input: t.In(2), output: out, rcvr: reflect.ValueOf(rcvr), fun: m.Func, }, nil }
func main() { var i int var recvDir reflect.ChanDir = reflect.RecvDir var chanOf reflect.Type = reflect.ChanOf(recvDir, reflect.TypeOf(i)) fmt.Println(chanOf.Kind()) // chan fmt.Println(chanOf.ChanDir()) // <-chan fmt.Println(chanOf.String()) // <-chan int var i1 int var recvDir1 reflect.ChanDir = reflect.SendDir var chanOf1 reflect.Type = reflect.ChanOf(recvDir1, reflect.TypeOf(i1)) fmt.Println(chanOf1.Kind(), chanOf1.ChanDir(), chanOf1.String()) // chan chan<- chan<- int var i2 int var recvDir2 reflect.ChanDir = reflect.BothDir var chanOf2 reflect.Type = reflect.ChanOf(recvDir2, reflect.TypeOf(i2)) fmt.Println(chanOf2.Kind(), chanOf2.ChanDir(), chanOf2.String()) // chan chan chan int var i3 string var recvDir3 reflect.ChanDir = reflect.BothDir var chanOf3 reflect.Type = reflect.ChanOf(recvDir3, reflect.TypeOf(i3)) fmt.Println(chanOf3.Kind(), chanOf3.ChanDir(), chanOf3.String()) // chan chan chan string }
// unify attempts to satisfy a pair of types, where the `param` type is the // expected type of a function argument and the `input` type is the known // type of a function argument. The `param` type may be parametric (that is, // it may contain a type that is convertible to TypeVariable) but the // `input` type may *not* be parametric. // // Any failure to unify the two types results in a panic. // // The end result of unification is a type environment: a set of substitutions // from type variable to a Go type. func (tp typePair) unify(param, input reflect.Type) error { if tyname := tyvarName(input); len(tyname) > 0 { return tp.error("Type variables are not allowed in the types of " + "arguments.") } if tyname := tyvarName(param); len(tyname) > 0 { if cur, ok := tp.tyenv[tyname]; ok && cur != input { return tp.error("Type variable %s expected type '%s' but got '%s'.", tyname, cur, input) } else if !ok { tp.tyenv[tyname] = input } return nil } if param.Kind() != input.Kind() { return tp.error("Cannot unify different kinds of types '%s' and '%s'.", param, input) } switch param.Kind() { case reflect.Array: return tp.unify(param.Elem(), input.Elem()) case reflect.Chan: if param.ChanDir() != input.ChanDir() { return tp.error("Cannot unify '%s' with '%s' "+ "(channel directions are different: '%s' != '%s').", param, input, param.ChanDir(), input.ChanDir()) } return tp.unify(param.Elem(), input.Elem()) case reflect.Func: if param.NumIn() != input.NumIn() || param.NumOut() != input.NumOut() { return tp.error("Cannot unify '%s' with '%s'.", param, input) } for i := 0; i < param.NumIn(); i++ { if err := tp.unify(param.In(i), input.In(i)); err != nil { return err } } for i := 0; i < param.NumOut(); i++ { if err := tp.unify(param.Out(i), input.Out(i)); err != nil { return err } } case reflect.Map: if err := tp.unify(param.Key(), input.Key()); err != nil { return err } return tp.unify(param.Elem(), input.Elem()) case reflect.Ptr: return tp.unify(param.Elem(), input.Elem()) case reflect.Slice: return tp.unify(param.Elem(), input.Elem()) } // The only other container types are Interface and Struct. // I am unsure about what to do with interfaces. Mind is fuzzy. // Structs? I don't think it really makes much sense to use type // variables inside of them. return nil }
// GetName of a type. func (opts *GenOpts) GetName(t reflect.Type) string { name := t.Name() if name != "" { pkg, _ := packageAndName(t) // Handle the case the type is in the package we are generating code for. if pkg == "" || pkg == opts.PkgName { return name } return fmt.Sprintf("%s.%s", pkg, name) } switch t.Kind() { case reflect.Ptr: return fmt.Sprintf("*%s", opts.GetName(t.Elem())) case reflect.Map: return fmt.Sprintf("map[%s]%s", opts.GetName(t.Key()), opts.GetName(t.Elem())) case reflect.Slice: return fmt.Sprintf("[]%s", opts.GetName(t.Elem())) case reflect.Chan: return fmt.Sprintf("%s %s", t.ChanDir().String(), opts.GetName(t.Elem())) case reflect.Array: return fmt.Sprintf("[%d]%s", t.Len(), opts.GetName(t.Elem())) case reflect.Func: inputs := make([]string, t.NumIn()) for i := range inputs { inputs[i] = opts.GetName(t.In(i)) } outputs := make([]string, t.NumOut()) for i := range outputs { outputs[i] = opts.GetName(t.Out(i)) } out := strings.Join(outputs, ", ") if len(outputs) > 1 { out = fmt.Sprintf("(%s)", out) } return fmt.Sprintf("func (%s) %s", strings.Join(inputs, ", "), out) default: return t.String() } }
func typeFromType(t reflect.Type) (Type, error) { // Hack workaround for https://golang.org/issue/3853. // This explicit check should not be necessary. if t == byteType { return PredeclaredType("byte"), nil } if imp := t.PkgPath(); imp != "" { return &NamedType{ Package: imp, Type: t.Name(), }, nil } // only unnamed or predeclared types after here // Lots of types have element types. Let's do the parsing and error checking for all of them. var elemType Type switch t.Kind() { case reflect.Array, reflect.Chan, reflect.Map, reflect.Ptr, reflect.Slice: var err error elemType, err = typeFromType(t.Elem()) if err != nil { return nil, err } } switch t.Kind() { case reflect.Array: return &ArrayType{ Len: t.Len(), Type: elemType, }, nil case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.String: return PredeclaredType(t.Kind().String()), nil case reflect.Chan: var dir ChanDir switch t.ChanDir() { case reflect.RecvDir: dir = RecvDir case reflect.SendDir: dir = SendDir } return &ChanType{ Dir: dir, Type: elemType, }, nil case reflect.Func: in, variadic, out, err := funcArgsFromType(t) if err != nil { return nil, err } return &FuncType{ In: in, Out: out, Variadic: variadic, }, nil case reflect.Interface: // Two special interfaces. if t.NumMethod() == 0 { return PredeclaredType("interface{}"), nil } if t == errorType { return PredeclaredType("error"), nil } case reflect.Map: kt, err := typeFromType(t.Key()) if err != nil { return nil, err } return &MapType{ Key: kt, Value: elemType, }, nil case reflect.Ptr: return &PointerType{ Type: elemType, }, nil case reflect.Slice: return &ArrayType{ Len: -1, Type: elemType, }, nil case reflect.Struct: if t.NumField() == 0 { return PredeclaredType("struct{}"), nil } } // TODO: Struct, UnsafePointer return nil, fmt.Errorf("can't yet turn %v (%v) into a model.Type", t, t.Kind()) }