From ae07aa605539f692f7745cbd52de6e283e996ec7 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 31 Jul 2013 12:49:34 -0700 Subject: [PATCH] go.tools/go/types: return typed bool for comma-ok expressions Also: determine default types for assignments of the form: _ = x . R=adonovan CC=golang-dev https://golang.org/cl/12186043 --- go/types/api_test.go | 23 ++++++++--------- go/types/assignments.go | 56 ++++++++++++++++++++++++++++------------- go/types/check.go | 8 +++--- go/types/stmt.go | 1 + 4 files changed, 55 insertions(+), 33 deletions(-) diff --git a/go/types/api_test.go b/go/types/api_test.go index 8faa40284f..e491953eb4 100644 --- a/go/types/api_test.go +++ b/go/types/api_test.go @@ -34,19 +34,23 @@ func TestCommaOkTypes(t *testing.T) { var tests = []struct { src string expr string // comma-ok expression string - typ Type // type of first comma-ok value + typ string // typestring of comma-ok value }{ {`package p; var x interface{}; var _, _ = x.(int)`, `x.(int)`, - Typ[Int], + `(int, bool)`, }, - {`package p; var m map[string]complex128; var _, _ = m["foo"]`, + {`package p; var x interface{}; func _() { _, _ = x.(int) }`, + `x.(int)`, + `(int, bool)`, + }, + {`package p; type mybool bool; var m map[string]complex128; var b mybool; func _() { _, b = m["foo"] }`, `m["foo"]`, - Typ[Complex128], + `(complex128, p.mybool)`, }, {`package p; var c chan string; var _, _ = <-c`, `<-c`, - Typ[String], + `(string, bool)`, }, } @@ -75,13 +79,8 @@ func TestCommaOkTypes(t *testing.T) { } // check that type is correct - got, _ := typ.(*Tuple) - want := NewTuple( - NewVar(token.NoPos, nil, "", test.typ), - NewVar(token.NoPos, nil, "", Typ[UntypedBool]), - ) - if got == nil || !identicalTuples(got, want) { - t.Errorf("%s: got %s; want %s", path, got, want) + if got := typ.String(); got != test.typ { + t.Errorf("%s: got %s; want %s", path, got, test.typ) } } } diff --git a/go/types/assignments.go b/go/types/assignments.go index 224e8b3b60..ca68ca0a1d 100644 --- a/go/types/assignments.go +++ b/go/types/assignments.go @@ -72,12 +72,12 @@ func (check *checker) initConst(lhs *Const, x *operand) { lhs.val = x.val } -func (check *checker) initVar(lhs *Var, x *operand) { +func (check *checker) initVar(lhs *Var, x *operand) Type { if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] { if lhs.typ == nil { lhs.typ = Typ[Invalid] } - return // nothing else to check + return nil // nothing else to check } // If the lhs doesn't have a type yet, use the type of x. @@ -88,7 +88,7 @@ func (check *checker) initVar(lhs *Var, x *operand) { if typ == Typ[UntypedNil] { check.errorf(x.pos(), "use of untyped nil") lhs.typ = Typ[Invalid] - return // nothing else to check + return nil // nothing else to check } typ = defaultType(typ) } @@ -99,30 +99,45 @@ func (check *checker) initVar(lhs *Var, x *operand) { if x.mode != invalid { check.errorf(x.pos(), "cannot initialize variable %s (type %s) with %s", lhs.Name(), lhs.typ, x) } + return nil } + + return lhs.typ } -func (check *checker) assignVar(lhs ast.Expr, x *operand) { +func (check *checker) assignVar(lhs ast.Expr, x *operand) Type { if x.mode == invalid || x.typ == Typ[Invalid] { - return + return nil } // Don't evaluate lhs if it is the blank identifier. if ident, _ := lhs.(*ast.Ident); ident != nil && ident.Name == "_" { check.recordObject(ident, nil) - check.updateExprType(x.expr, x.typ, true) // rhs has its final type - return + // If the lhs is untyped, determine the default type. + // The spec is unclear about this, but gc appears to + // do this. + typ := x.typ + if isUntyped(typ) { + // convert untyped types to default types + if typ == Typ[UntypedNil] { + check.errorf(x.pos(), "use of untyped nil") + return nil // nothing else to check + } + typ = defaultType(typ) + } + check.updateExprType(x.expr, typ, true) // rhs has its final type + return typ } var z operand check.expr(&z, lhs) if z.mode == invalid || z.typ == Typ[Invalid] { - return + return nil } if z.mode == constant || z.mode == value { check.errorf(z.pos(), "cannot assign to non-variable %s", &z) - return + return nil } // TODO(gri) z.mode can also be valueok which in some cases is ok (maps) @@ -132,7 +147,10 @@ func (check *checker) assignVar(lhs ast.Expr, x *operand) { if x.mode != invalid { check.errorf(x.pos(), "cannot assign %s to %s", x, &z) } + return nil } + + return z.typ } func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) { @@ -182,15 +200,17 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) { if allowCommaOk && x.mode == valueok && l == 2 { // comma-ok expression - check.recordCommaOkType(rhs, x.typ) - x.mode = value - check.initVar(lhs[0], &x) + t1 := check.initVar(lhs[0], &x) x.mode = value x.expr = rhs x.typ = Typ[UntypedBool] - check.initVar(lhs[1], &x) + t2 := check.initVar(lhs[1], &x) + + if t1 != nil && t2 != nil { + check.recordCommaOkTypes(rhs, t1, t2) + } return } } @@ -245,15 +265,17 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) { if x.mode == valueok && l == 2 { // comma-ok expression - check.recordCommaOkType(rhs, x.typ) - x.mode = value - check.assignVar(lhs[0], &x) + t1 := check.assignVar(lhs[0], &x) x.mode = value x.expr = rhs x.typ = Typ[UntypedBool] - check.assignVar(lhs[1], &x) + t2 := check.assignVar(lhs[1], &x) + + if t1 != nil && t2 != nil { + check.recordCommaOkTypes(rhs, t1, t2) + } return } } diff --git a/go/types/check.go b/go/types/check.go index 529b95bcf2..14aff8174c 100644 --- a/go/types/check.go +++ b/go/types/check.go @@ -82,14 +82,14 @@ func (check *checker) recordTypeAndValue(x ast.Expr, typ Type, val exact.Value) } } -func (check *checker) recordCommaOkType(x ast.Expr, typ Type) { - assert(x != nil && typ != nil) +func (check *checker) recordCommaOkTypes(x ast.Expr, t1, t2 Type) { + assert(x != nil && !isUntyped(t1) && !isUntyped(t2) && isBoolean(t2)) if m := check.Types; m != nil { assert(m[x] != nil) // should have been recorded already pos := x.Pos() m[x] = NewTuple( - NewVar(pos, check.pkg, "", typ), - NewVar(pos, check.pkg, "", Typ[UntypedBool]), + NewVar(pos, check.pkg, "", t1), + NewVar(pos, check.pkg, "", t2), ) } } diff --git a/go/types/stmt.go b/go/types/stmt.go index e0f7006711..03a8950fdc 100644 --- a/go/types/stmt.go +++ b/go/types/stmt.go @@ -509,6 +509,7 @@ func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) { val = typ.elt case *Chan: key = typ.elt + val = Typ[Invalid] if typ.dir&ast.RECV == 0 { check.errorf(x.pos(), "cannot range over send-only channel %s", &x) // ok to continue