]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: typechecking conversions, part 1 (non-constants)
authorRobert Griesemer <gri@golang.org>
Wed, 23 Jan 2013 18:57:18 +0000 (10:57 -0800)
committerRobert Griesemer <gri@golang.org>
Wed, 23 Jan 2013 18:57:18 +0000 (10:57 -0800)
R=adonovan
CC=golang-dev
https://golang.org/cl/7103058

src/pkg/go/types/conversions.go

index 5a6bea80d178aab4e3ca67427c0d5c0be0643920..0c7c9f706dd921257c4764c72d0dca925576291f 100644 (file)
@@ -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
+}