github.com/gmemcc/yaegi@v0.12.1-0.20221128122509-aa99124c5d16/internal/cmd/extract/types/conversions.go (about) 1 // Copyright 2012 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // This file implements typechecking of conversions. 6 7 package types 8 9 import ( 10 "go/constant" 11 "unicode" 12 ) 13 14 // Conversion type-checks the conversion T(x). 15 // The result is in x. 16 func (check *Checker) conversion(x *operand, T Type) { 17 constArg := x.mode == constant_ 18 19 var ok bool 20 switch { 21 case constArg && isConstType(T): 22 // constant conversion 23 switch t := T.Underlying().(*Basic); { 24 case representableConst(x.val, check, t, &x.val): 25 ok = true 26 case isInteger(x.typ) && isString(t): 27 codepoint := unicode.ReplacementChar 28 if i, ok := constant.Uint64Val(x.val); ok && i <= unicode.MaxRune { 29 codepoint = rune(i) 30 } 31 x.val = constant.MakeString(string(codepoint)) 32 ok = true 33 } 34 case x.convertibleTo(check, T): 35 // non-constant conversion 36 x.mode = value 37 ok = true 38 } 39 40 if !ok { 41 check.errorf(x, _InvalidConversion, "cannot convert %s to %s", x, T) 42 x.mode = invalid 43 return 44 } 45 46 // The conversion argument types are final. For untyped values the 47 // conversion provides the type, per the spec: "A constant may be 48 // given a type explicitly by a constant declaration or conversion,...". 49 if isUntyped(x.typ) { 50 final := T 51 // - For conversions to interfaces, use the argument's default type. 52 // - For conversions of untyped constants to non-constant types, also 53 // use the default type (e.g., []byte("foo") should report string 54 // not []byte as type for the constant "foo"). 55 // - Keep untyped nil for untyped nil arguments. 56 // - For integer to string conversions, keep the argument type. 57 // (See also the TODO below.) 58 if IsInterface(T) || constArg && !isConstType(T) { 59 final = Default(x.typ) 60 } else if isInteger(x.typ) && isString(T) { 61 final = x.typ 62 } 63 check.updateExprType(x.expr, final, true) 64 } 65 66 x.typ = T 67 } 68 69 // TODO(gri) convertibleTo checks if T(x) is valid. It assumes that the type 70 // of x is fully known, but that's not the case for say string(1<<s + 1.0): 71 // Here, the type of 1<<s + 1.0 will be UntypedFloat which will lead to the 72 // (correct!) refusal of the conversion. But the reported error is essentially 73 // "cannot convert untyped float value to string", yet the correct error (per 74 // the spec) is that we cannot shift a floating-point value: 1 in 1<<s should 75 // be converted to UntypedFloat because of the addition of 1.0. Fixing this 76 // is tricky because we'd have to run updateExprType on the argument first. 77 // (Issue #21982.) 78 79 // convertibleTo reports whether T(x) is valid. 80 // The check parameter may be nil if convertibleTo is invoked through an 81 // exported API call, i.e., when all methods have been type-checked. 82 func (x *operand) convertibleTo(check *Checker, T Type) bool { 83 // "x is assignable to T" 84 if ok, _ := x.assignableTo(check, T, nil); ok { 85 return true 86 } 87 88 // "x's type and T have identical underlying types if tags are ignored" 89 V := x.typ 90 Vu := V.Underlying() 91 Tu := T.Underlying() 92 if check.identicalIgnoreTags(Vu, Tu) { 93 return true 94 } 95 96 // "x's type and T are unnamed pointer types and their pointer base types 97 // have identical underlying types if tags are ignored" 98 if V, ok := V.(*Pointer); ok { 99 if T, ok := T.(*Pointer); ok { 100 if check.identicalIgnoreTags(V.base.Underlying(), T.base.Underlying()) { 101 return true 102 } 103 } 104 } 105 106 // "x's type and T are both integer or floating point types" 107 if (isInteger(V) || isFloat(V)) && (isInteger(T) || isFloat(T)) { 108 return true 109 } 110 111 // "x's type and T are both complex types" 112 if isComplex(V) && isComplex(T) { 113 return true 114 } 115 116 // "x is an integer or a slice of bytes or runes and T is a string type" 117 if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) { 118 return true 119 } 120 121 // "x is a string and T is a slice of bytes or runes" 122 if isString(V) && isBytesOrRunes(Tu) { 123 return true 124 } 125 126 // package unsafe: 127 // "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer" 128 if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) { 129 return true 130 } 131 // "and vice versa" 132 if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) { 133 return true 134 } 135 136 return false 137 } 138 139 func isUintptr(typ Type) bool { 140 t, ok := typ.Underlying().(*Basic) 141 return ok && t.kind == Uintptr 142 } 143 144 func isUnsafePointer(typ Type) bool { 145 // TODO(gri): Is this (typ.Underlying() instead of just typ) correct? 146 // The spec does not say so, but gc claims it is. See also 147 // issue 6326. 148 t, ok := typ.Underlying().(*Basic) 149 return ok && t.kind == UnsafePointer 150 } 151 152 func isPointer(typ Type) bool { 153 _, ok := typ.Underlying().(*Pointer) 154 return ok 155 } 156 157 func isBytesOrRunes(typ Type) bool { 158 if s, ok := typ.(*Slice); ok { 159 t, ok := s.elem.Underlying().(*Basic) 160 return ok && (t.kind == Byte || t.kind == Rune) 161 } 162 return false 163 }