mirror of
https://github.com/golang/go.git
synced 2025-05-06 08:03:03 +00:00
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
This commit is contained in:
parent
323b856f4d
commit
ae07aa6055
@ -34,19 +34,23 @@ func TestCommaOkTypes(t *testing.T) {
|
|||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
src string
|
src string
|
||||||
expr string // comma-ok expression 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)`,
|
{`package p; var x interface{}; var _, _ = x.(int)`,
|
||||||
`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"]`,
|
`m["foo"]`,
|
||||||
Typ[Complex128],
|
`(complex128, p.mybool)`,
|
||||||
},
|
},
|
||||||
{`package p; var c chan string; var _, _ = <-c`,
|
{`package p; var c chan string; var _, _ = <-c`,
|
||||||
`<-c`,
|
`<-c`,
|
||||||
Typ[String],
|
`(string, bool)`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,13 +79,8 @@ func TestCommaOkTypes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check that type is correct
|
// check that type is correct
|
||||||
got, _ := typ.(*Tuple)
|
if got := typ.String(); got != test.typ {
|
||||||
want := NewTuple(
|
t.Errorf("%s: got %s; want %s", path, got, test.typ)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,12 +72,12 @@ func (check *checker) initConst(lhs *Const, x *operand) {
|
|||||||
lhs.val = x.val
|
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 x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
|
||||||
if lhs.typ == nil {
|
if lhs.typ == nil {
|
||||||
lhs.typ = Typ[Invalid]
|
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.
|
// 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] {
|
if typ == Typ[UntypedNil] {
|
||||||
check.errorf(x.pos(), "use of untyped nil")
|
check.errorf(x.pos(), "use of untyped nil")
|
||||||
lhs.typ = Typ[Invalid]
|
lhs.typ = Typ[Invalid]
|
||||||
return // nothing else to check
|
return nil // nothing else to check
|
||||||
}
|
}
|
||||||
typ = defaultType(typ)
|
typ = defaultType(typ)
|
||||||
}
|
}
|
||||||
@ -99,30 +99,45 @@ func (check *checker) initVar(lhs *Var, x *operand) {
|
|||||||
if x.mode != invalid {
|
if x.mode != invalid {
|
||||||
check.errorf(x.pos(), "cannot initialize variable %s (type %s) with %s", lhs.Name(), lhs.typ, x)
|
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] {
|
if x.mode == invalid || x.typ == Typ[Invalid] {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't evaluate lhs if it is the blank identifier.
|
// Don't evaluate lhs if it is the blank identifier.
|
||||||
if ident, _ := lhs.(*ast.Ident); ident != nil && ident.Name == "_" {
|
if ident, _ := lhs.(*ast.Ident); ident != nil && ident.Name == "_" {
|
||||||
check.recordObject(ident, nil)
|
check.recordObject(ident, nil)
|
||||||
check.updateExprType(x.expr, x.typ, true) // rhs has its final type
|
// If the lhs is untyped, determine the default type.
|
||||||
return
|
// 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
|
var z operand
|
||||||
check.expr(&z, lhs)
|
check.expr(&z, lhs)
|
||||||
if z.mode == invalid || z.typ == Typ[Invalid] {
|
if z.mode == invalid || z.typ == Typ[Invalid] {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if z.mode == constant || z.mode == value {
|
if z.mode == constant || z.mode == value {
|
||||||
check.errorf(z.pos(), "cannot assign to non-variable %s", &z)
|
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)
|
// 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 {
|
if x.mode != invalid {
|
||||||
check.errorf(x.pos(), "cannot assign %s to %s", x, &z)
|
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) {
|
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 {
|
if allowCommaOk && x.mode == valueok && l == 2 {
|
||||||
// comma-ok expression
|
// comma-ok expression
|
||||||
check.recordCommaOkType(rhs, x.typ)
|
|
||||||
|
|
||||||
x.mode = value
|
x.mode = value
|
||||||
check.initVar(lhs[0], &x)
|
t1 := check.initVar(lhs[0], &x)
|
||||||
|
|
||||||
x.mode = value
|
x.mode = value
|
||||||
x.expr = rhs
|
x.expr = rhs
|
||||||
x.typ = Typ[UntypedBool]
|
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
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,15 +265,17 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) {
|
|||||||
|
|
||||||
if x.mode == valueok && l == 2 {
|
if x.mode == valueok && l == 2 {
|
||||||
// comma-ok expression
|
// comma-ok expression
|
||||||
check.recordCommaOkType(rhs, x.typ)
|
|
||||||
|
|
||||||
x.mode = value
|
x.mode = value
|
||||||
check.assignVar(lhs[0], &x)
|
t1 := check.assignVar(lhs[0], &x)
|
||||||
|
|
||||||
x.mode = value
|
x.mode = value
|
||||||
x.expr = rhs
|
x.expr = rhs
|
||||||
x.typ = Typ[UntypedBool]
|
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
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
func (check *checker) recordCommaOkTypes(x ast.Expr, t1, t2 Type) {
|
||||||
assert(x != nil && typ != nil)
|
assert(x != nil && !isUntyped(t1) && !isUntyped(t2) && isBoolean(t2))
|
||||||
if m := check.Types; m != nil {
|
if m := check.Types; m != nil {
|
||||||
assert(m[x] != nil) // should have been recorded already
|
assert(m[x] != nil) // should have been recorded already
|
||||||
pos := x.Pos()
|
pos := x.Pos()
|
||||||
m[x] = NewTuple(
|
m[x] = NewTuple(
|
||||||
NewVar(pos, check.pkg, "", typ),
|
NewVar(pos, check.pkg, "", t1),
|
||||||
NewVar(pos, check.pkg, "", Typ[UntypedBool]),
|
NewVar(pos, check.pkg, "", t2),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -509,6 +509,7 @@ func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) {
|
|||||||
val = typ.elt
|
val = typ.elt
|
||||||
case *Chan:
|
case *Chan:
|
||||||
key = typ.elt
|
key = typ.elt
|
||||||
|
val = Typ[Invalid]
|
||||||
if typ.dir&ast.RECV == 0 {
|
if typ.dir&ast.RECV == 0 {
|
||||||
check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
|
check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
|
||||||
// ok to continue
|
// ok to continue
|
||||||
|
Loading…
x
Reference in New Issue
Block a user