// Copyright 2012 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.// This file implements typechecking of conversions.package typesimport ()// Conversion type-checks the conversion T(x).// The result is in x.func ( *Checker) ( *operand, Type) { := .mode == constant_ := func( Type, *constant.Value) bool {switch , := under().(*Basic); {case == nil:// nothing to docaserepresentableConst(.val, , , ):returntruecaseisInteger(.typ) && isString(): := unicode.ReplacementCharif , := constant.Uint64Val(.val); && <= unicode.MaxRune { = rune() }if != nil { * = constant.MakeString(string()) }returntrue }returnfalse }varboolvarstringswitch {case && isConstType():// constant conversion = (, &.val)case && isTypeParam():// x is convertible to T if it is convertible // to each specific type in the type set of T. // If T's type set is empty, or if it doesn't // have specific types, constant x cannot be // converted. = .(*TypeParam).underIs(func( Type) bool {// u is nil if there are no specific type termsif == nil { = .sprintf("%s does not contain specific types", )returnfalse }ifisString(.typ) && isBytesOrRunes() {returntrue }if !(, nil) { = .sprintf("cannot convert %s to %s (in %s)", , , )returnfalse }returntrue }) .mode = value// type parameters are not constantscase .convertibleTo(, , &):// non-constant conversion = true .mode = value }if ! {// TODO(rfindley): use types2-style error reporting here.ifcompilerErrorMessages {if != "" {// Add colon at end of line if we have a following cause. .errorf(, _InvalidConversion, "cannot convert %s to type %s:\n\t%s", , , ) } else { .errorf(, _InvalidConversion, "cannot convert %s to type %s", , ) } } else {if != "" { .errorf(, _InvalidConversion, "cannot convert %s to %s (%s)", , , ) } else { .errorf(, _InvalidConversion, "cannot convert %s to %s", , ) } } .mode = invalidreturn }// The conversion argument types are final. For untyped values the // conversion provides the type, per the spec: "A constant may be // given a type explicitly by a constant declaration or conversion,...".ifisUntyped(.typ) { := // - For conversions to interfaces, use the argument's default type. // - For conversions of untyped constants to non-constant types, also // use the default type (e.g., []byte("foo") should report string // not []byte as type for the constant "foo"). // - Keep untyped nil for untyped nil arguments. // - For constant integer to string conversions, keep the argument type. // (See also the TODO below.)ifIsInterface() && !isTypeParam() || && !isConstType() || .isNil() { = Default(.typ) // default type of untyped nil is untyped nil } elseif .mode == constant_ && isInteger(.typ) && allString() { = .typ } .updateExprType(.expr, , true) } .typ = }// TODO(gri) convertibleTo checks if T(x) is valid. It assumes that the type// of x is fully known, but that's not the case for say string(1<<s + 1.0):// Here, the type of 1<<s + 1.0 will be UntypedFloat which will lead to the// (correct!) refusal of the conversion. But the reported error is essentially// "cannot convert untyped float value to string", yet the correct error (per// the spec) is that we cannot shift a floating-point value: 1 in 1<<s should// be converted to UntypedFloat because of the addition of 1.0. Fixing this// is tricky because we'd have to run updateExprType on the argument first.// (Issue #21982.)// convertibleTo reports whether T(x) is valid. In the failure case, *cause// may be set to the cause for the failure.// The check parameter may be nil if convertibleTo is invoked through an// exported API call, i.e., when all methods have been type-checked.func ( *operand) ( *Checker, Type, *string) bool {// "x is assignable to T"if , := .assignableTo(, , ); {returntrue }// "V and T have identical underlying types if tags are ignored // and V and T are not type parameters" := .typ := under() := under() , := .(*TypeParam) , := .(*TypeParam)ifIdenticalIgnoreTags(, ) && == nil && == nil {returntrue }// "V and T are unnamed pointer types and their pointer base types // have identical underlying types if tags are ignored // and their pointer base types are not type parameters"if , := .(*Pointer); {if , := .(*Pointer); {ifIdenticalIgnoreTags(under(.base), under(.base)) && !isTypeParam(.base) && !isTypeParam(.base) {returntrue } } }// "V and T are both integer or floating point types"ifisIntegerOrFloat() && isIntegerOrFloat() {returntrue }// "V and T are both complex types"ifisComplex() && isComplex() {returntrue }// "V is an integer or a slice of bytes or runes and T is a string type"if (isInteger() || isBytesOrRunes()) && isString() {returntrue }// "V is a string and T is a slice of bytes or runes"ifisString() && isBytesOrRunes() {returntrue }// package unsafe: // "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"if (isPointer() || isUintptr()) && isUnsafePointer() {returntrue }// "and vice versa"ifisUnsafePointer() && (isPointer() || isUintptr()) {returntrue }// "V a slice, T is a pointer-to-array type, // and the slice and array types have identical element types."if , := .(*Slice); != nil {if , := .(*Pointer); != nil {if , := under(.Elem()).(*Array); != nil {ifIdentical(.Elem(), .Elem()) {if == nil || .allowVersion(.pkg, 1, 17) {returntrue }if != nil { * = "conversion of slices to array pointers requires go1.17 or later" } } } } }// optimization: if we don't have type parameters, we're doneif == nil && == nil {returnfalse } := func( string, ...any) {if != nil && != nil { := .sprintf(, ...)if * != "" { += "\n\t" + * } * = } }// generic cases with specific type terms // (generic operands cannot be constants, so we can ignore x.val)switch {case != nil && != nil: := * // don't clobber outer xreturn .is(func( *term) bool {if == nil {returnfalse// no specific types } .typ = .typreturn .is(func( *term) bool {if == nil {returnfalse// no specific types }if !.(, .typ, ) { ("cannot convert %s (in %s) to %s (in %s)", .typ, , .typ, )returnfalse }returntrue }) })case != nil: := * // don't clobber outer xreturn .is(func( *term) bool {if == nil {returnfalse// no specific types } .typ = .typif !.(, , ) { ("cannot convert %s (in %s) to %s", .typ, , )returnfalse }returntrue })case != nil:return .is(func( *term) bool {if == nil {returnfalse// no specific types }if !.(, .typ, ) { ("cannot convert %s to %s (in %s)", .typ, .typ, )returnfalse }returntrue }) }returnfalse}func ( Type) bool { , := under().(*Basic)return != nil && .kind == Uintptr}func ( Type) bool { , := under().(*Basic)return != nil && .kind == UnsafePointer}func ( Type) bool { , := under().(*Pointer)return}func ( Type) bool {if , := under().(*Slice); != nil { , := under(.elem).(*Basic)return != nil && (.kind == Byte || .kind == Rune) }returnfalse}
The pages are generated with Goldsv0.4.9. (GOOS=linux GOARCH=amd64)