From: Robert Griesemer Date: Wed, 23 Jan 2013 18:57:18 +0000 (-0800) Subject: go/types: typechecking conversions, part 1 (non-constants) X-Git-Tag: go1.1rc2~1300 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=2e15f4b8c49b2e240d4852d39610956a3473da3c;p=gostls13.git go/types: typechecking conversions, part 1 (non-constants) R=adonovan CC=golang-dev https://golang.org/cl/7103058 --- diff --git a/src/pkg/go/types/conversions.go b/src/pkg/go/types/conversions.go index 5a6bea80d1..0c7c9f706d 100644 --- a/src/pkg/go/types/conversions.go +++ b/src/pkg/go/types/conversions.go @@ -28,10 +28,18 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota goto Error } - // TODO(gri) fix this - implement all checks and constant evaluation - if x.mode != constant || !isConstType(typ) { + if x.mode == constant && isConstType(typ) { + // constant conversion + // TODO(gri) implement this + } else { + // non-constant conversion + if !x.isConvertible(typ) { + check.invalidOp(conv.Pos(), "cannot convert %s to %s", x, typ) + goto Error + } x.mode = value } + x.expr = conv x.typ = typ return @@ -39,3 +47,82 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota Error: x.mode = invalid } + +func (x *operand) isConvertible(T Type) bool { + // "x is assignable to T" + if x.isAssignable(T) { + return true + } + + // "x's type and T have identical underlying types" + V := x.typ + Vu := underlying(V) + Tu := underlying(T) + if isIdentical(Vu, Tu) { + return true + } + + // "x's type and T are unnamed pointer types and their pointer base types have identical underlying types" + if V, ok := V.(*Pointer); ok { + if T, ok := T.(*Pointer); ok { + if isIdentical(underlying(V.Base), underlying(T.Base)) { + return true + } + } + } + + // "x's type and T are both integer or floating point types" + if (isInteger(V) || isFloat(V)) && (isInteger(T) || isFloat(T)) { + return true + } + + // "x's type and T are both complex types" + if isComplex(V) && isComplex(T) { + return true + } + + // "x is an integer or a slice of bytes or runes and T is a string type" + if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) { + return true + } + + // "x is a string and T is a slice of bytes or runes" + if isString(V) && isBytesOrRunes(Tu) { + return true + } + + // package unsafe: + // "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer" + if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) { + return true + } + // "and vice versa" + if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) { + return true + } + + return false +} + +func isUintptr(typ Type) bool { + t, ok := typ.(*Basic) + return ok && t.Kind == Uintptr +} + +func isUnsafePointer(typ Type) bool { + t, ok := typ.(*Basic) + return ok && t.Kind == UnsafePointer +} + +func isPointer(typ Type) bool { + _, ok := typ.(*Pointer) + return ok +} + +func isBytesOrRunes(typ Type) bool { + if s, ok := typ.(*Slice); ok { + t, ok := underlying(s.Elt).(*Basic) + return ok && (t.Kind == Byte || t.Kind == Rune) + } + return false +}