func main() { for _, test := range tests { var got, want exact.Value var a []string switch a = strings.Split(test, " "); len(a) { case 4: got = doOp(nil, optab[a[0]], val(a[1])) want = val(a[3]) case 5: got = doOp(val(a[0]), optab[a[1]], val(a[2])) want = val(a[4]) default: fmt.Printf("invalid test case: %s\n", test) continue } if !exact.Compare(got, token.EQL, want) { fmt.Printf("%s failed: got %s; want %s\n", test, got, want) } else { expr := strings.Join(a[:len(a)-2], " ") fmt.Printf("%s gave: %s\n", expr, got) } } }
func (check *Checker) comparison(x, y *operand, op token.Token) { // spec: "In any comparison, the first operand must be assignable // to the type of the second operand, or vice versa." err := "" if x.assignableTo(check.conf, y.typ) || y.assignableTo(check.conf, x.typ) { defined := false switch op { case token.EQL, token.NEQ: // spec: "The equality operators == and != apply to operands that are comparable." defined = Comparable(x.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ) case token.LSS, token.LEQ, token.GTR, token.GEQ: // spec: The ordering operators <, <=, >, and >= apply to operands that are ordered." defined = isOrdered(x.typ) default: unreachable() } if !defined { typ := x.typ if x.isNil() { typ = y.typ } err = check.sprintf("operator %s not defined for %s", op, typ) } } else { err = check.sprintf("mismatched types %s and %s", x.typ, y.typ) } if err != "" { check.errorf(x.pos(), "cannot compare %s %s %s (%s)", x.expr, op, y.expr, err) x.mode = invalid return } if x.mode == constant && y.mode == constant { x.val = exact.MakeBool(exact.Compare(x.val, op, y.val)) // The operands are never materialized; no need to update // their types. } else { x.mode = value // The operands have now their final types, which at run- // time will be materialized. Update the expression trees. // If the current types are untyped, the materialized type // is the respective default type. check.updateExprType(x.expr, defaultType(x.typ), true) check.updateExprType(y.expr, defaultType(y.typ), true) } // spec: "Comparison operators compare two operands and yield // an untyped boolean value." x.typ = Typ[UntypedBool] }
func doOp(x exact.Value, op token.Token, y exact.Value) (z exact.Value) { defer panicHandler(&z) if x == nil { return exact.UnaryOp(op, y, -1) } switch op { case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ: return exact.MakeBool(exact.Compare(x, op, y)) case token.SHL, token.SHR: s, _ := exact.Int64Val(y) return exact.Shift(x, op, uint(s)) default: return exact.BinaryOp(x, op, y) } }
func checkConstValue(t *testing.T, prog *ssa.Program, obj *types.Const) { c := prog.ConstValue(obj) // fmt.Printf("ConstValue(%s) = %s\n", obj, c) // debugging if c == nil { t.Errorf("ConstValue(%s) == nil", obj) return } if !types.Identical(c.Type(), obj.Type()) { t.Errorf("ConstValue(%s).Type() == %s", obj, c.Type()) return } if obj.Name() != "nil" { if !exact.Compare(c.Value, token.EQL, obj.Val()) { t.Errorf("ConstValue(%s).Value (%s) != %s", obj, c.Value, obj.Val()) return } } }
func (check *checker) comparison(x, y *operand, op token.Token) { // TODO(gri) deal with interface vs non-interface comparison valid := false if x.isAssignableTo(check.conf, y.typ) || y.isAssignableTo(check.conf, x.typ) { switch op { case token.EQL, token.NEQ: valid = isComparable(x.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ) case token.LSS, token.LEQ, token.GTR, token.GEQ: valid = isOrdered(x.typ) default: unreachable() } } if !valid { check.invalidOp(x.pos(), "cannot compare %s %s %s", x, op, y) x.mode = invalid return } if x.mode == constant && y.mode == constant { x.val = exact.MakeBool(exact.Compare(x.val, op, y.val)) // The operands are never materialized; no need to update // their types. } else { x.mode = value // The operands have now their final types, which at run- // time will be materialized. Update the expression trees. // If the current types are untyped, the materialized type // is the respective default type. check.updateExprType(x.expr, defaultType(x.typ), true) check.updateExprType(y.expr, defaultType(y.typ), true) } // spec: "Comparison operators compare two operands and yield // an untyped boolean value." x.typ = Typ[UntypedBool] }
func evalAction(n ast.Node) exact.Value { switch e := n.(type) { case *ast.BasicLit: return val(e.Value) case *ast.BinaryExpr: x := evalAction(e.X) if x == nil { return nil } y := evalAction(e.Y) if y == nil { return nil } switch e.Op { case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ: return exact.MakeBool(exact.Compare(x, e.Op, y)) case token.SHL, token.SHR: s, _ := exact.Int64Val(y) return exact.Shift(x, e.Op, uint(s)) default: return exact.BinaryOp(x, e.Op, y) } case *ast.UnaryExpr: return exact.UnaryOp(e.Op, evalAction(e.X), -1) case *ast.CallExpr: fmt.Printf("Can't handle call (%s) yet at pos %d\n", e.Fun, e.Pos()) return nil case *ast.Ident: fmt.Printf("Can't handle Ident %s here at pos %d\n", e.Name, e.Pos()) return nil case *ast.ParenExpr: return evalAction(e.X) default: fmt.Println("Can't handle") fmt.Printf("n: %s, e: %s\n", n, e) return nil } }
// matchExpr reports whether pattern x matches y. // // If tr.allowWildcards, Idents in x that refer to parameters are // treated as wildcards, and match any y that is assignable to the // parameter type; matchExpr records this correspondence in tr.env. // Otherwise, matchExpr simply reports whether the two trees are // equivalent. // // A wildcard appearing more than once in the pattern must // consistently match the same tree. // func (tr *Transformer) matchExpr(x, y ast.Expr) bool { if x == nil && y == nil { return true } if x == nil || y == nil { return false } x = unparen(x) y = unparen(y) // Is x a wildcard? (a reference to a 'before' parameter) if x, ok := x.(*ast.Ident); ok && x != nil && tr.allowWildcards { if xobj, ok := tr.info.Uses[x].(*types.Var); ok && tr.wildcards[xobj] { return tr.matchWildcard(xobj, y) } } // Object identifiers (including pkg-qualified ones) // are handled semantically, not syntactically. xobj := isRef(x, &tr.info) yobj := isRef(y, &tr.info) if xobj != nil { return xobj == yobj } if yobj != nil { return false } // TODO(adonovan): audit: we cannot assume these ast.Exprs // contain non-nil pointers. e.g. ImportSpec.Name may be a // nil *ast.Ident. if reflect.TypeOf(x) != reflect.TypeOf(y) { return false } switch x := x.(type) { case *ast.Ident: log.Fatalf("unexpected Ident: %s", astString(tr.fset, x)) case *ast.BasicLit: y := y.(*ast.BasicLit) xval := exact.MakeFromLiteral(x.Value, x.Kind) yval := exact.MakeFromLiteral(y.Value, y.Kind) return exact.Compare(xval, token.EQL, yval) case *ast.FuncLit: // func literals (and thus statement syntax) never match. return false case *ast.CompositeLit: y := y.(*ast.CompositeLit) return (x.Type == nil) == (y.Type == nil) && (x.Type == nil || tr.matchType(x.Type, y.Type)) && tr.matchExprs(x.Elts, y.Elts) case *ast.SelectorExpr: y := y.(*ast.SelectorExpr) return tr.matchExpr(x.X, y.X) && tr.info.Selections[x].Obj() == tr.info.Selections[y].Obj() case *ast.IndexExpr: y := y.(*ast.IndexExpr) return tr.matchExpr(x.X, y.X) && tr.matchExpr(x.Index, y.Index) case *ast.SliceExpr: y := y.(*ast.SliceExpr) return tr.matchExpr(x.X, y.X) && tr.matchExpr(x.Low, y.Low) && tr.matchExpr(x.High, y.High) && tr.matchExpr(x.Max, y.Max) && x.Slice3 == y.Slice3 case *ast.TypeAssertExpr: y := y.(*ast.TypeAssertExpr) return tr.matchExpr(x.X, y.X) && tr.matchType(x.Type, y.Type) case *ast.CallExpr: y := y.(*ast.CallExpr) match := tr.matchExpr // function call if tr.info.IsType(x.Fun) { match = tr.matchType // type conversion } return x.Ellipsis.IsValid() == y.Ellipsis.IsValid() && match(x.Fun, y.Fun) && tr.matchExprs(x.Args, y.Args) case *ast.StarExpr: y := y.(*ast.StarExpr) return tr.matchExpr(x.X, y.X) case *ast.UnaryExpr: y := y.(*ast.UnaryExpr) return x.Op == y.Op && tr.matchExpr(x.X, y.X) case *ast.BinaryExpr: y := y.(*ast.BinaryExpr) return x.Op == y.Op && tr.matchExpr(x.X, y.X) && tr.matchExpr(x.Y, y.Y) case *ast.KeyValueExpr: y := y.(*ast.KeyValueExpr) return tr.matchExpr(x.Key, y.Key) && tr.matchExpr(x.Value, y.Value) } panic(fmt.Sprintf("unhandled AST node type: %T", x)) }