// compare must be called for each comparison x==y. func (f *Finder) compare(x, y types.Type) { if types.AssignableTo(x, y) { f.assign(y, x) } else if types.AssignableTo(y, x) { f.assign(x, y) } }
func (c *typeFilterConstraint) solve(a *analysis, n *node, delta nodeset) { for ifaceObj := range delta { tDyn, _, indirect := a.taggedValue(ifaceObj) if indirect { // TODO(adonovan): we'll need to implement this // when we start creating indirect tagged objects. panic("indirect tagged object") } if types.AssignableTo(tDyn, c.typ) { if a.addLabel(c.dst, ifaceObj) { a.addWork(c.dst) } } } }
// typeAssert must be called for each type assertion x.(T) where x has // interface type I. func (f *Finder) typeAssert(I, T types.Type) { // Type assertions are slightly subtle, because they are allowed // to be "impossible", e.g. // // var x interface{f()} // _ = x.(interface{f()int}) // legal // // (In hindsight, the language spec should probably not have // allowed this, but it's too late to fix now.) // // This means that a type assert from I to T isn't exactly a // constraint that T is assignable to I, but for a refactoring // tool it is a conditional constraint that, if T is assignable // to I before a refactoring, it should remain so after. if types.AssignableTo(T, I) { f.assign(I, T) } }
func (tr *Transformer) matchWildcard(xobj *types.Var, y ast.Expr) bool { name := xobj.Name() if tr.verbose { fmt.Fprintf(os.Stderr, "%s: wildcard %s -> %s?: ", tr.fset.Position(y.Pos()), name, astString(tr.fset, y)) } // Check that y is assignable to the declared type of the param. if yt := tr.info.TypeOf(y); !types.AssignableTo(yt, xobj.Type()) { if tr.verbose { fmt.Fprintf(os.Stderr, "%s not assignable to %s\n", yt, xobj.Type()) } return false } // A wildcard matches any expression. // If it appears multiple times in the pattern, it must match // the same expression each time. if old, ok := tr.env[name]; ok { // found existing binding tr.allowWildcards = false r := tr.matchExpr(old, y) if tr.verbose { fmt.Fprintf(os.Stderr, "%t secondary match, primary was %s\n", r, astString(tr.fset, old)) } tr.allowWildcards = true return r } if tr.verbose { fmt.Fprintf(os.Stderr, "primary match\n") } tr.env[name] = y // record binding return true }
func WriteProgramCode(pkgs []*Archive, importContext *ImportContext, w *SourceMapFilter) { mainPkg := pkgs[len(pkgs)-1] minify := mainPkg.Minified declsByObject := make(map[string][]*Decl) var pendingDecls []*Decl for _, pkg := range pkgs { for i := range pkg.Declarations { d := &pkg.Declarations[i] if len(d.DceFilters) == 0 { pendingDecls = append(pendingDecls, d) continue } for _, f := range d.DceFilters { o := string(pkg.ImportPath) + ":" + string(f) declsByObject[o] = append(declsByObject[o], d) } } } for len(pendingDecls) != 0 { d := pendingDecls[len(pendingDecls)-1] pendingDecls = pendingDecls[:len(pendingDecls)-1] for _, dep := range d.DceDeps { o := string(dep) if decls, ok := declsByObject[o]; ok { delete(declsByObject, o) name := strings.Split(o, ":")[1] for _, d := range decls { for i, f := range d.DceFilters { if string(f) == name { d.DceFilters[i] = d.DceFilters[len(d.DceFilters)-1] d.DceFilters = d.DceFilters[:len(d.DceFilters)-1] break } } if len(d.DceFilters) == 0 { pendingDecls = append(pendingDecls, d) } } } } } w.Write([]byte("\"use strict\";\n(function() {\n\n")) w.Write(removeWhitespace([]byte(strings.TrimSpace(prelude)), minify)) w.Write([]byte("\n")) // write packages for _, pkg := range pkgs { WritePkgCode(pkg, minify, w) } // write interfaces allTypeNames := []*types.TypeName{types.New("error").(*types.Named).Obj()} for _, pkg := range pkgs { scope := importContext.Packages[string(pkg.ImportPath)].Scope() for _, name := range scope.Names() { if typeName, isTypeName := scope.Lookup(name).(*types.TypeName); isTypeName { if _, notUsed := declsByObject[string(pkg.ImportPath)+":"+name]; !notUsed { allTypeNames = append(allTypeNames, typeName) } } } } for _, t := range allTypeNames { if in, isInterface := t.Type().Underlying().(*types.Interface); isInterface { if in.Empty() { continue } implementedBy := make(map[string]bool, 0) for _, other := range allTypeNames { otherType := other.Type() switch otherType.Underlying().(type) { case *types.Interface: // skip case *types.Struct: if types.AssignableTo(otherType, in) { implementedBy[fmt.Sprintf("$packages[\"%s\"].%s", other.Pkg().Path(), other.Name())] = true } if types.AssignableTo(types.NewPointer(otherType), in) { implementedBy[fmt.Sprintf("$packages[\"%s\"].%s.Ptr", other.Pkg().Path(), other.Name())] = true } default: if types.AssignableTo(otherType, in) { implementedBy[fmt.Sprintf("$packages[\"%s\"].%s", other.Pkg().Path(), other.Name())] = true } if types.AssignableTo(types.NewPointer(otherType), in) { implementedBy[fmt.Sprintf("$ptrType($packages[\"%s\"].%s)", other.Pkg().Path(), other.Name())] = true } } } list := make([]string, 0, len(implementedBy)) for ref := range implementedBy { list = append(list, ref) } sort.Strings(list) var target string switch t.Name() { case "error": target = "$error" default: target = fmt.Sprintf("$packages[\"%s\"].%s", t.Pkg().Path(), t.Name()) } w.Write(removeWhitespace([]byte(fmt.Sprintf("%s.implementedBy = [%s];\n", target, strings.Join(list, ", "))), minify)) } } for _, pkg := range pkgs { w.Write([]byte("$packages[\"" + string(pkg.ImportPath) + "\"].init();\n")) } w.Write([]byte("$packages[\"" + string(mainPkg.ImportPath) + "\"].main();\n\n})();\n")) }
// NewTransformer returns a transformer based on the specified template, // a package containing "before" and "after" functions as described // in the package documentation. // func NewTransformer(fset *token.FileSet, template *loader.PackageInfo, verbose bool) (*Transformer, error) { // Check the template. beforeSig := funcSig(template.Pkg, "before") if beforeSig == nil { return nil, fmt.Errorf("no 'before' func found in template") } afterSig := funcSig(template.Pkg, "after") if afterSig == nil { return nil, fmt.Errorf("no 'after' func found in template") } // TODO(adonovan): should we also check the names of the params match? if !types.Identical(afterSig, beforeSig) { return nil, fmt.Errorf("before %s and after %s functions have different signatures", beforeSig, afterSig) } templateFile := template.Files[0] for _, imp := range templateFile.Imports { if imp.Name != nil && imp.Name.Name == "." { // Dot imports are currently forbidden. We // make the simplifying assumption that all // imports are regular, without local renames. //TODO document return nil, fmt.Errorf("dot-import (of %s) in template", imp.Path.Value) } } var beforeDecl, afterDecl *ast.FuncDecl for _, decl := range templateFile.Decls { if decl, ok := decl.(*ast.FuncDecl); ok { switch decl.Name.Name { case "before": beforeDecl = decl case "after": afterDecl = decl } } } before, err := soleExpr(beforeDecl) if err != nil { return nil, fmt.Errorf("before: %s", err) } after, err := soleExpr(afterDecl) if err != nil { return nil, fmt.Errorf("after: %s", err) } wildcards := make(map[*types.Var]bool) for i := 0; i < beforeSig.Params().Len(); i++ { wildcards[beforeSig.Params().At(i)] = true } // checkExprTypes returns an error if Tb (type of before()) is not // safe to replace with Ta (type of after()). // // Only superficial checks are performed, and they may result in both // false positives and negatives. // // Ideally, we would only require that the replacement be assignable // to the context of a specific pattern occurrence, but the type // checker doesn't record that information and it's complex to deduce. // A Go type cannot capture all the constraints of a given expression // context, which may include the size, constness, signedness, // namedness or constructor of its type, and even the specific value // of the replacement. (Consider the rule that array literal keys // must be unique.) So we cannot hope to prove the safety of a // transformation in general. Tb := template.TypeOf(before) Ta := template.TypeOf(after) if types.AssignableTo(Tb, Ta) { // safe: replacement is assignable to pattern. } else if tuple, ok := Tb.(*types.Tuple); ok && tuple.Len() == 0 { // safe: pattern has void type (must appear in an ExprStmt). } else { return nil, fmt.Errorf("%s is not a safe replacement for %s", Ta, Tb) } tr := &Transformer{ fset: fset, verbose: verbose, wildcards: wildcards, allowWildcards: true, seenInfos: make(map[*types.Info]bool), importedObjs: make(map[types.Object]*ast.SelectorExpr), before: before, after: after, } // Combine type info from the template and input packages, and // type info for the synthesized ASTs too. This saves us // having to book-keep where each ast.Node originated as we // construct the resulting hybrid AST. // // TODO(adonovan): move type utility methods of PackageInfo to // types.Info, or at least into go/types.typeutil. tr.info.Info = types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), Selections: make(map[*ast.SelectorExpr]*types.Selection), } mergeTypeInfo(&tr.info.Info, &template.Info) // Compute set of imported objects required by after(). // TODO reject dot-imports in pattern ast.Inspect(after, func(n ast.Node) bool { if n, ok := n.(*ast.SelectorExpr); ok { if _, ok := tr.info.Selections[n]; !ok { // qualified ident obj := tr.info.Uses[n.Sel] tr.importedObjs[obj] = n return false // prune } } return true // recur }) return tr, nil }
func (l langType) EmitTypeInfo() string { ret := "class TypeInfo{\n" pte := pogo.TypesEncountered pteKeys := pogo.TypesEncountered.Keys() ret += "public static function getName(id:Int):String {\nswitch(id){" + "\n" for k := range pteKeys { v := pte.At(pteKeys[k]) ret += "case " + fmt.Sprintf("%d", v) + `: return "` + pteKeys[k].String() + `";` + "\n" } ret += `default: return "UNKNOWN";}}` + "\n" ret += "public static function typeString(i:Interface):String {\nreturn getName(i.typ);\n}\n" ret += "public static function getId(name:String):Int {\nswitch(name){" + "\n" for k := range pteKeys { v := pte.At(pteKeys[k]) ret += `case "` + pteKeys[k].String() + `": return ` + fmt.Sprintf("%d", v) + `;` + "\n" } ret += "default: return -1;}}\n" //emulation of: func IsAssignableTo(V, T Type) bool ret += "public static function isAssignableTo(v:Int,t:Int):Bool {\nif(v==t) return true;\nswitch(v){" + "\n" for V := range pteKeys { v := pte.At(pteKeys[V]) ret += `case ` + fmt.Sprintf("%d", v) + `: switch(t){` + "\n" for T := range pteKeys { t := pte.At(pteKeys[T]) if v != t && types.AssignableTo(pteKeys[V], pteKeys[T]) { ret += `case ` + fmt.Sprintf("%d", t) + `: return true;` + "\n" } } ret += "default: return false;}\n" } ret += "default: return false;}}\n" //emulation of: func IsIdentical(x, y Type) bool ret += "public static function isIdentical(v:Int,t:Int):Bool {\nif(v==t) return true;\nswitch(v){" + "\n" for V := range pteKeys { v := pte.At(pteKeys[V]) ret += `case ` + fmt.Sprintf("%d", v) + `: switch(t){` + "\n" for T := range pteKeys { t := pte.At(pteKeys[T]) if v != t && types.Identical(pteKeys[V], pteKeys[T]) { ret += `case ` + fmt.Sprintf("%d", t) + `: return true;` + "\n" } } ret += "default: return false;}\n" } ret += "default: return false;}}\n" //function to answer the question is the type a concrete value? ret += "public static function isConcrete(t:Int):Bool {\nswitch(t){" + "\n" for T := range pteKeys { t := pte.At(pteKeys[T]) switch pteKeys[T].Underlying().(type) { case *types.Interface: ret += `case ` + fmt.Sprintf("%d", t) + `: return false;` + "\n" default: ret += `case ` + fmt.Sprintf("%d", t) + `: return true;` + "\n" } } ret += "default: return false;}}\n" // function to give the zero value for each type ret += "public static function zeroValue(t:Int):Dynamic {\nswitch(t){" + "\n" for T := range pteKeys { t := pte.At(pteKeys[T]) ret += `case ` + fmt.Sprintf("%d", t) + `: return ` ret += l.LangType(pteKeys[T], true, "EmitTypeInfo()") + ";\n" } ret += "default: return null;}}\n" ret += "public static function method(t:Int,m:String):Dynamic {\nswitch(t){" + "\n" tta := pogo.TypesWithMethodSets() //[]types.Type for T := range tta { t := pte.At(tta[T]) if t != nil { // it is used? ret += `case ` + fmt.Sprintf("%d", t) + `: switch(m){` + "\n" ms := types.NewMethodSet(tta[T]) for m := 0; m < ms.Len(); m++ { funcObj, ok := ms.At(m).Obj().(*types.Func) pkgName := "unknown" if ok && funcObj.Pkg() != nil { line := "" ss := strings.Split(funcObj.Pkg().Name(), "/") pkgName = ss[len(ss)-1] if strings.HasPrefix(pkgName, "_") { // exclude functions in haxe for now // TODO NoOp for now... so haxe types cant be "Involked" when held in interface types // *** need to deal with getters and setters // *** also with calling parameters which are different for a Haxe API } else { line = `case "` + funcObj.Name() + `": return ` fnToCall := l.LangName(ms.At(m).Recv().String(), funcObj.Name()) ovPkg, _, isOv := l.PackageOverloaded(funcObj.Pkg().Name()) if isOv { fnToCall = strings.Replace(fnToCall, "_"+funcObj.Pkg().Name()+"_", "_"+ovPkg+"_", -1) // NOTE this is not a fool-proof method } line += `Go_` + fnToCall + `.call` + "; " } ret += line } ret += fmt.Sprintf("// %v %v %v %v\n", ms.At(m).Obj().Name(), ms.At(m).Kind(), ms.At(m).Index(), ms.At(m).Indirect()) } ret += "default:}\n" } } ret += "default:}\n Scheduler.panicFromHaxe( " + `"no method found!"` + "); return null;}\n" // TODO improve error return ret + "}" }
// Implements displays the "implements" relation as it pertains to the // selected type. // func implements(o *Oracle, qpos *QueryPos) (queryResult, error) { // Find the selected type. // TODO(adonovan): fix: make it work on qualified Idents too. path, action := findInterestingNode(qpos.info, qpos.path) if action != actionType { return nil, fmt.Errorf("no type here") } T := qpos.info.TypeOf(path[0].(ast.Expr)) if T == nil { return nil, fmt.Errorf("no type here") } // Find all named types, even local types (which can have // methods via promotion) and the built-in "error". // // TODO(adonovan): include all packages in PTA scope too? // i.e. don't reduceScope? // var allNamed []types.Type for _, info := range o.typeInfo { for _, obj := range info.Defs { if obj, ok := obj.(*types.TypeName); ok { allNamed = append(allNamed, obj.Type()) } } } allNamed = append(allNamed, types.Universe.Lookup("error").Type()) var msets types.MethodSetCache // Test each named type. var to, from, fromPtr []types.Type for _, U := range allNamed { if isInterface(T) { if msets.MethodSet(T).Len() == 0 { continue // empty interface } if isInterface(U) { if msets.MethodSet(U).Len() == 0 { continue // empty interface } // T interface, U interface if !types.Identical(T, U) { if types.AssignableTo(U, T) { to = append(to, U) } if types.AssignableTo(T, U) { from = append(from, U) } } } else { // T interface, U concrete if types.AssignableTo(U, T) { to = append(to, U) } else if pU := types.NewPointer(U); types.AssignableTo(pU, T) { to = append(to, pU) } } } else if isInterface(U) { if msets.MethodSet(U).Len() == 0 { continue // empty interface } // T concrete, U interface if types.AssignableTo(T, U) { from = append(from, U) } else if pT := types.NewPointer(T); types.AssignableTo(pT, U) { fromPtr = append(fromPtr, U) } } } var pos interface{} = qpos if nt, ok := deref(T).(*types.Named); ok { pos = nt.Obj() } // Sort types (arbitrarily) to ensure test determinism. sort.Sort(typesByString(to)) sort.Sort(typesByString(from)) sort.Sort(typesByString(fromPtr)) return &implementsResult{T, pos, to, from, fromPtr}, nil }