// typeOf returns a distinct single-bit value that represents the type of n. // // Various implementations were benchmarked with BenchmarkNewInspector: // GOGC=off // - type switch 4.9-5.5ms 2.1ms // - binary search over a sorted list of types 5.5-5.9ms 2.5ms // - linear scan, frequency-ordered list 5.9-6.1ms 2.7ms // - linear scan, unordered list 6.4ms 2.7ms // - hash table 6.5ms 3.1ms // A perfect hash seemed like overkill. // // The compiler's switch statement is the clear winner // as it produces a binary tree in code, // with constant conditions and good branch prediction. // (Sadly it is the most verbose in source code.) // Binary search suffered from poor branch prediction. // private static ulong typeOf(ast.Node n) { // Fast path: nearly half of all nodes are identifiers. { ptr <ast.Ident> (_, ok) = n._ <ptr <ast.Ident> >(); if (ok) { return(1L << (int)(nIdent)); } // These cases include all nodes encountered by ast.Inspect. } // These cases include all nodes encountered by ast.Inspect. switch (n.type()) { case ptr <ast.ArrayType> _: return(1L << (int)(nArrayType)); break; case ptr <ast.AssignStmt> _: return(1L << (int)(nAssignStmt)); break; case ptr <ast.BadDecl> _: return(1L << (int)(nBadDecl)); break; case ptr <ast.BadExpr> _: return(1L << (int)(nBadExpr)); break; case ptr <ast.BadStmt> _: return(1L << (int)(nBadStmt)); break; case ptr <ast.BasicLit> _: return(1L << (int)(nBasicLit)); break; case ptr <ast.BinaryExpr> _: return(1L << (int)(nBinaryExpr)); break; case ptr <ast.BlockStmt> _: return(1L << (int)(nBlockStmt)); break; case ptr <ast.BranchStmt> _: return(1L << (int)(nBranchStmt)); break; case ptr <ast.CallExpr> _: return(1L << (int)(nCallExpr)); break; case ptr <ast.CaseClause> _: return(1L << (int)(nCaseClause)); break; case ptr <ast.ChanType> _: return(1L << (int)(nChanType)); break; case ptr <ast.CommClause> _: return(1L << (int)(nCommClause)); break; case ptr <ast.Comment> _: return(1L << (int)(nComment)); break; case ptr <ast.CommentGroup> _: return(1L << (int)(nCommentGroup)); break; case ptr <ast.CompositeLit> _: return(1L << (int)(nCompositeLit)); break; case ptr <ast.DeclStmt> _: return(1L << (int)(nDeclStmt)); break; case ptr <ast.DeferStmt> _: return(1L << (int)(nDeferStmt)); break; case ptr <ast.Ellipsis> _: return(1L << (int)(nEllipsis)); break; case ptr <ast.EmptyStmt> _: return(1L << (int)(nEmptyStmt)); break; case ptr <ast.ExprStmt> _: return(1L << (int)(nExprStmt)); break; case ptr <ast.Field> _: return(1L << (int)(nField)); break; case ptr <ast.FieldList> _: return(1L << (int)(nFieldList)); break; case ptr <ast.File> _: return(1L << (int)(nFile)); break; case ptr <ast.ForStmt> _: return(1L << (int)(nForStmt)); break; case ptr <ast.FuncDecl> _: return(1L << (int)(nFuncDecl)); break; case ptr <ast.FuncLit> _: return(1L << (int)(nFuncLit)); break; case ptr <ast.FuncType> _: return(1L << (int)(nFuncType)); break; case ptr <ast.GenDecl> _: return(1L << (int)(nGenDecl)); break; case ptr <ast.GoStmt> _: return(1L << (int)(nGoStmt)); break; case ptr <ast.Ident> _: return(1L << (int)(nIdent)); break; case ptr <ast.IfStmt> _: return(1L << (int)(nIfStmt)); break; case ptr <ast.ImportSpec> _: return(1L << (int)(nImportSpec)); break; case ptr <ast.IncDecStmt> _: return(1L << (int)(nIncDecStmt)); break; case ptr <ast.IndexExpr> _: return(1L << (int)(nIndexExpr)); break; case ptr <ast.InterfaceType> _: return(1L << (int)(nInterfaceType)); break; case ptr <ast.KeyValueExpr> _: return(1L << (int)(nKeyValueExpr)); break; case ptr <ast.LabeledStmt> _: return(1L << (int)(nLabeledStmt)); break; case ptr <ast.MapType> _: return(1L << (int)(nMapType)); break; case ptr <ast.Package> _: return(1L << (int)(nPackage)); break; case ptr <ast.ParenExpr> _: return(1L << (int)(nParenExpr)); break; case ptr <ast.RangeStmt> _: return(1L << (int)(nRangeStmt)); break; case ptr <ast.ReturnStmt> _: return(1L << (int)(nReturnStmt)); break; case ptr <ast.SelectStmt> _: return(1L << (int)(nSelectStmt)); break; case ptr <ast.SelectorExpr> _: return(1L << (int)(nSelectorExpr)); break; case ptr <ast.SendStmt> _: return(1L << (int)(nSendStmt)); break; case ptr <ast.SliceExpr> _: return(1L << (int)(nSliceExpr)); break; case ptr <ast.StarExpr> _: return(1L << (int)(nStarExpr)); break; case ptr <ast.StructType> _: return(1L << (int)(nStructType)); break; case ptr <ast.SwitchStmt> _: return(1L << (int)(nSwitchStmt)); break; case ptr <ast.TypeAssertExpr> _: return(1L << (int)(nTypeAssertExpr)); break; case ptr <ast.TypeSpec> _: return(1L << (int)(nTypeSpec)); break; case ptr <ast.TypeSwitchStmt> _: return(1L << (int)(nTypeSwitchStmt)); break; case ptr <ast.UnaryExpr> _: return(1L << (int)(nUnaryExpr)); break; case ptr <ast.ValueSpec> _: return(1L << (int)(nValueSpec)); break; } return(0L); }
private static ast.Visitor Visit(this simplifier s, ast.Node node) { switch (node.type()) { case ptr <ast.CompositeLit> n: var outer = n; ast.Expr keyType = default; ast.Expr eltType = default; switch (outer.Type.type()) { case ptr <ast.ArrayType> typ: eltType = typ.Elt; break; case ptr <ast.MapType> typ: keyType = typ.Key; eltType = typ.Value; break; } if (eltType != null) { reflect.Value ktyp = default; if (keyType != null) { ktyp = reflect.ValueOf(keyType); } var typ = reflect.ValueOf(eltType); foreach (var(i, x) in outer.Elts) { var px = _addr_outer.Elts[i]; // look at value of indexed/named elements { ptr <ast.KeyValueExpr> (t, ok) = x._ <ptr <ast.KeyValueExpr> >(); if (ok) { if (keyType != null) { s.simplifyLiteral(ktyp, keyType, t.Key, _addr_t.Key); } x = t.Value; px = _addr_t.Value; } } s.simplifyLiteral(typ, eltType, x, px); } // node was simplified - stop walk (there are no subnodes to simplify) return(null); } break; case ptr <ast.SliceExpr> n: if (n.Max != null) { // - 3-index slices always require the 2nd and 3rd index break; } { ptr <ast.Ident> (s, _) = n.X._ <ptr <ast.Ident> >(); if (s != null && s.Obj != null) { // the array/slice object is a single, resolved identifier { ptr <ast.CallExpr> (call, _) = n.High._ <ptr <ast.CallExpr> >(); if (call != null && len(call.Args) == 1L && !call.Ellipsis.IsValid()) { // the high expression is a function call with a single argument { ptr <ast.Ident> (fun, _) = call.Fun._ <ptr <ast.Ident> >(); if (fun != null && fun.Name == "len" && fun.Obj == null) { // the function called is "len" and it is not locally defined; and // because we don't have dot imports, it must be the predefined len() { ptr <ast.Ident> (arg, _) = call.Args[0L]._ <ptr <ast.Ident> >(); if (arg != null && arg.Obj == s.Obj) { // the len argument is the array/slice object n.High = null; } } } } } } } // Note: We could also simplify slice expressions of the form s[0:b] to s[:b] // but we leave them as is since sometimes we want to be very explicit // about the lower bound. // An example where the 0 helps: // x, y, z := b[0:2], b[2:4], b[4:6] // An example where it does not: // x, y := b[:n], b[n:] } // Note: We could also simplify slice expressions of the form s[0:b] to s[:b] // but we leave them as is since sometimes we want to be very explicit // about the lower bound. // An example where the 0 helps: // x, y, z := b[0:2], b[2:4], b[4:6] // An example where it does not: // x, y := b[:n], b[n:] break; case ptr <ast.RangeStmt> n: if (isBlank(n.Value)) { n.Value = null; } if (isBlank(n.Key) && n.Value == null) { n.Key = null; } break; } return(s); }